2017-02-14 106 views
0

我是新来的异步编程,并试图编写一些测试代码,看看我能用它做些什么。如何知道所有的异步/等待完成?

下面是我的测试控制台应用程序代码,调用一个异步方法,它会将一些现有的.docx文件发布到我的web api并转换为pdf。

static void Main(string[] args) 
    { 
     //async testing - 5 files batch 
     Console.WriteLine("Job Start:"); 

     for (int i = 0; i < 5; i++) 
     { 
      var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open); 

      //delegate 
      Action act = async() => 
      { 
       await test(filestream, i.ToString()); 
       filestream.Dispose(); 
      }; 

      act(); 
     } 

     Console.WriteLine("End."); 
     Console.ReadLine(); 
    } 

和方法:

static async Task<int> test(FileStream fs, string id) 
    { 
     var content = new StreamContent(fs); 
     var client = new HttpClient(); 

     var startTime = DateTime.Now; 

     //post to web api 
     var response = await client.PostAsync("http://localhost:50348/PDFConvert/Convert?name=" + id, content); 

     var ts = DateTime.Now - startTime; 

     Console.WriteLine("Time Cost: " + ts.TotalSeconds.ToString() + " seconds, " + response.Content.ReadAsStringAsync().Result.Replace("\"", "")); 

     client.Dispose(); 
     return 0; 

    } 

它的工作原理,到目前为止,但一个问题是 “结束”。在控制台窗口的“Job Start:”后面打印,然后等待输出。

所以我的问题是:
1.如何在所有异步/等待完成后打印“结束”消息?
2.不确定这是否是最佳做法,因此对现有代码的任何建议?

非常感谢。

回答

3

你不应该做async void,除非你正在编写一个事件处理程序,如果你强制它成为Action代表,导致async void委托。

将您的代理更改为Func<Task>,然后您可以执行一系列任务并对其执行WaitAll

static void Main(string[] args) 
{ 
    //async testing - 5 files batch 
    Console.WriteLine("Job Start:"); 

    var tasks = new Task[5]; 

    for (int i = 0; i < 5; i++) 
    { 
     var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open); 

     //Moved i.ToString() out of the delegate to fix a potential bug with variable capture. 
     var stringToPrint = i.ToString() 

     //delegate 
     Func<Task> act = async() => 
     { 
      await test(filestream, stringToPrint); 
      filestream.Dispose(); 
     }; 

     var task = act(); 
     tasks[i] = task; 
    } 

    //In a non console program you will likely want to do "await Task.WhenAll(tasks);" instead. 
    Task.WaitAll(tasks); 

    Console.WriteLine("End."); 
    Console.ReadLine(); 
} 
+0

它适合我,谢谢。 – blk25t

+0

@ blk25t那么你应该标记接受的答案。 –

2

我是新来的异步编程

我建议我async intro接着我async best practices文章。

请勿使用async lambda表示Action委托类型;这导致了async void方法。 async void方法的缺点之一是,你无法轻易知道它们何时完成。

异步不会与控制台应用程序自然网格化。您可以阻止异步代码的主线程,但是这不是在任何类型的其他应用程序使用的良好格局:

static void Main(string[] args) 
{ 
    Console.WriteLine("Job Start:"); 

    MainAsync().GetAwaiter().GetResult(); 

    Console.WriteLine("End."); 
    Console.ReadLine(); 
} 

static async Task MainAsync() 
{ 
    for (int i = 0; i < 5; i++) 
    { 
    using (var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open)) 
     await test(filestream, i.ToString()); 
    } 
} 

更新由于评论

既然你想运行任务同步,你应该遵循的指导,我async intro post和使用Task.WhenAll

static async Task MainAsync() 
{ 
    var tasks = Enumerable.Range(0, 5).Select(TestFilesAsync).ToList(); 
    await Task.WhenAll(tasks); 
} 

static async Task TestFilesAsync(int i) 
{ 
    using (var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open)) 
    await test(filestream, i.ToString()); 
} 
+0

谢谢,它在'结束'部分工作,但出现另一个问题:任务一个接一个地运行,而不是并行运行。我认为控制台应用程序不是一个很好的示例,所以我会尝试用winforms编写一些东西。 – blk25t

+0

@ blk25t:我更新了您的更新问题的答案。 –

0

可以使用异步拉姆达LINQ的选择方法内部遍历您的任务列表,然后等待它们完成

var list = Enumerable.Range(1,5).Select (
      async i => { 
       var filestream = File.Open(@"c:\pdftest\" + i.ToString() [email protected]".docx", FileMode.Open); 
       await test(filestream, i.ToString()); 
       filestream.Dispose(); 
       } 
    ); 
    Task.WaitAll (list.ToArray()); 

如果它不是一个控制台应用程序,但一个异步上下文,本可以使用WhenAll而不是WaitAll