2017-02-14 84 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