2017-02-14 4 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; 

    } 

それは、これまでに動作しますが、1つの問題は、 "エンド" です。コンソールウィンドウで「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ラムダをActionデリゲートタイプで使用しないでください。その結果、async voidメソッドになります。 async voidメソッドの欠点の1つは、完了すると簡単にわからないことです。

Asyncは、コンソールアプリケーションと自然にマッチングしません。あなたは、非同期コードのメインスレッドをブロックすることができますが、これは、他のあらゆる種類のアプリケーションで使用するための良好なパターンではありません:あなたはあなたを実行したいので

:をコメントすることにより

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

ありがとうございます。「終了」部分で動作しますが、別の問題が発生します。タスクは並列に実行されるのではなく、1つずつ実行されます。私はあなたの考えをコンソールアプリケーションはテストする良い例ではないので、私はwinformsに何かを書き込もうとします。 – blk25t

+0

@ blk25t:更新された質問の回答を更新しました。 –

0

あなたは、あなたのタスクリストを反復処理し、それはコンソールアプリが、非同期コンテキストでなかった場合は、それらが

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()); 

を完了するのを待つためにLINQの選択メソッド内非同期ラムダを使用することができますWaitAllの代わりにWhenAllを使用している可能性があります。

関連する問題