2016-09-08 6 views
-1

インターネットでは、async \ awaitはプログラミングの "天才的な革新"であることがよくあります。時にはそれはありますが、場合によっては、書かれる必要のあるコードを短縮できないと感じています。async/awaitシンタックスは、プログラマの手動スレッディングよりも本当に優れていますか、それほど役に立ちませんか?

私は3つの並列タスク(ダウンロード)が必要で、各ダウンロードの結果を処理したい場合(処理(コンソールへの出力SUM)は、ダウンロードごとに依存し、すべての結果を同時に必要とする場合# 1)、Iは、スレッドを使用してそれを行うことができますし、GetByteArray)が存在しないため、非同期を使用せずに/(としてここにいくつかの擬似コードを待つ:

using System; 
using System.Net.Http; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static int length = 0; 
     static HttpClient client = 
      new HttpClient() { MaxResponseContentBufferSize = 1000000 }; 
     static void Main(string[] args) 
     { 
      CreateMultipleTasksAsync(); 
      Console.ReadKey(); 
     } 
     static void CreateMultipleTasksAsync() 
     { 
      Thread th1 = new Thread(ProcessURLAsync); 
      th1.Start("http://msdn.microsoft.com"); 
      Thread th2 = new Thread(ProcessURLAsync); 
      th2.Start("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx"); 
      Thread th3 = new Thread(ProcessURLAsync); 
      th3.Start("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx"); 

      //#2 there I need results of all 3 downloads (so it will wait for all 3 downloads being completed) 
      Console.WriteLine("\r\n\r\nTotal bytes returned: {0}\r\n", Program.length); 
     } 
     static void ProcessURLAsync(object urlObj) 
     { 
      string url = (string)urlObj; 
      var length = client.GetByteArray(url).Length; 
    /*  //#1 there I need only a result of one current download (so it will wait only for current download being completed) 
      Console.WriteLine("\n{0,-58} {1}", url, length);*/ 
      Program.length =+ length; 
     } 
    } 
} 

またはasync /のawaitを使って、このようにそれを行うことができます。

using System; 
using System.Net.Http; 
using System.Threading.Tasks; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      CreateMultipleTasksAsync(); 
      Console.ReadKey(); 
     } 
     static async Task CreateMultipleTasksAsync() 
     {    
     HttpClient client = 
       new HttpClient() { MaxResponseContentBufferSize = 1000000 }; 
      Task<byte[]> download1 = 
       client.GetByteArrayAsync("http://msdn.microsoft.com"); 
      Task<byte[]> download2 = 
       client.GetByteArrayAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx"); 
      Task<byte[]> download3 = 
       client.GetByteArrayAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx"); 

      int length1 = (await download1).Length; 
      int length2 = (await download2).Length; 
      int length3 = (await download3).Length; 
      //#1 there I need results of all 3 downloads (so it will wait for all 3 downloads being completed) 
      Console.WriteLine("\r\n\r\nTotal bytes returned: {0}\r\n", length1 + length2 + length3); 
     } 
    } 
} 

とasync/await-wayは本当に短く、より良いですすべてのコードがコンパクトで、すべてが1つのメソッドCreateMultipleTasksAsyncにあり、私は追加のメソッドを作成してそれを委譲すべきではありません。

しかし、1回のダウンロード結果(#2のように他のダウンロードとは独立して)で何かをしたいのであれば、1つのコードを別の方法で取り出す必要があります。この方法。私はコードをこのように書くcant'tため

私は追加の方法が必要です。

... 
      Task<int> download1 = 
       ProcessURLAsync("http://msdn.microsoft.com", client); 
      Task<int> download2 = 
       ProcessURLAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client); 
      Task<int> download3 = 
       ProcessURLAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client); 

      int length1 = (await download1).Length; 
      //#3 
      Console.WriteLine("\n{0,-58} {1}", "http://msdn.microsoft.com", length1); 
      int length2 = (await download2).Length; 
      //#4 
      Console.WriteLine("\n{0,-58} {1}", "http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", length2); 
      int length3 = (await download3).Length; 
      //#5 
... 

このコードにdownload2の結果は常にdownload1の結果を処理した後(#4でコンソールに出力する)処理されるためdownload3(#3)とdownload2(#4)の両方の結果を処理した後(#3)、download3の結果は常に処理される(#5でコンソールに出力される)。この場合の出力順序は常に同じです(たとえダウンロード3がダウンロード1よりはるかに早く完了しても、とにかく#3は#3の後に表示されます)。

しかし、私はdownload3が早くdownload1その後、完成したとき、#5は#3の前に表示したい場合、私は追加の方法ProcessURLAsync作成することを強制しています:

using System; 
using System.Net.Http; 
using System.Threading.Tasks; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      CreateMultipleTasksAsync(); 
      Console.ReadKey(); 
     } 
     static async Task CreateMultipleTasksAsync() 
     {   
     HttpClient client = 
       new HttpClient() { MaxResponseContentBufferSize = 1000000 }; 
      Task<int> download1 = 
       ProcessURLAsync("http://msdn.microsoft.com", client); 
      Task<int> download2 = 
       ProcessURLAsync("http://msdn.microsoft.com/en-us/library/hh156528(VS.110).aspx", client); 
      Task<int> download3 = 
       ProcessURLAsync("http://msdn.microsoft.com/en-us/library/67w7t67f.aspx", client); 

      int length1 = await download1; 
      int length2 = await download2; 
      int length3 = await download3; 
      //#1 there I need results of all 3 downloads (so it will wait for all 3 downloads being completed) 
      Console.WriteLine("\r\n\r\nTotal bytes returned: {0}\r\n", length1 + length2 + length3); 
     } 
     static async Task<int> ProcessURLAsync(string url, HttpClient client) 
     { 
      var length = (await client.GetByteArrayAsync(url)).Length; 
      //#2 there I need only a result of one current download, independently from other downloads (so it will wait only for current download being completed) 
      Console.WriteLine("\n{0,-58} {1}", url, length); 
      return length; 
     } 
    } 
} 

をし、この場合はそうではありません単一のダウンロードの結果を処理するために追加のメソッドProcessURLAsyncを作成する必要があるので、非同期/待機ウェイがより優れている理由を明らかにします。したがって、私のコードは、非同期/待機なしの最初のサンプルのコードに非常に似ています。

async/await-wayが3つの理由で明らかにスレッドウェイより優れているという事実を無視した場合 - 1)オブジェクトとしてデリゲートにURLを渡してから文字列にキャストする必要があります.2) (デリゲートから値を返すことができないため)静的プロパティProgram.lengthを作成する必要なしに長さ(length1、length2、length3)を格納するローカル変数のみ、3)複数の引数を渡すことはできません代理人なので、ローカルではなく 'HttpClientクライアント'を静的にする必要があります。非同期/待機ウェイは、次のような場合にのみ実際に優れています。 2)、複数の並列タスクが必要ですが、それらを別々に処理する必要はありません(それらを完全に処理する必要があるだけです)。

どのような並列タスクも互いに依存せず、それらを別々に処理する必要がある場合、async/await構文の利点はありますか?

+0

ハンマーよりもハンマーハンマーが優れていますか?状況によって異なります。タスクはスレッドではないことを忘れないでください。 https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel/ – TyCobb

+2

投稿を編集して具体的にお尋ねください質問。あなたが達成したいことは非常に不明です...現在、あなたは不正な(スレッドセーフではない)コードを、いくつかの非同期タスクへの順次呼び出しと並行して実行されるスレッドと比較しています。そして、タスクを実行する理由はないと主張します。 –

+0

並行スレッドと非同期処理を待っているのは別のものです。あなたは正確に何を比較していますか? – David

答えて

2

あなたの最終的なコードは本当にスレッド化に匹敵しません。単一スレッドのSynchronizationContext(UIアプリケーションのデフォルト)を使用すると、ProcessURLAsync呼び出しは重複して実行されますが、並列に実行されることはありません。唯一の中断点はawaitキーワードの使用です。

これは、同期を追加することなく安全にUIにアクセスし、共有データ構造を更新できることを意味します。これにより、明示的なスレッド化と比較して、コードの長さと複雑さが大幅に削減されます。

(注:コメントに記載されているとおり、スレッド化された偽の同等に表示されるこの行は同期変数にアクセスしないため、競合状態になります。Program.length =+ length;これはスレッドが結果が印刷される前に終了してください。HttpClient clientオブジェクトがスレッドセーフでない場合は、さらに問題が発生する可能性があります)。

+0

*これは、スレッドが結果を印刷する前に終了するのを待つことの合計に加えて)* - すべてのスレッドの完了を待つことなくProgram.lengthの結果を出力することを意味するなら、私は最初の例をテストしませんでした。なぜなら、それはコンパイルできないからです。今私はエラーが表示されます。 – user3649516

1

ここにはいくつかのものがあります。

まず、UIアプリケーションとコンソールアプリケーションの同期コンテキストの違いにより、コンソールアプリケーションでの非同期動作が明らかに奇妙になる可能性があります。this articleを参照してください。

第2に、asyncは必ずしもマルチスレッドと同じではありません。 Task.Run(...)のようなことをすれば、スレッドプール上で確実に実行されます。しかし、 "標準的な"非同期操作は同じではありません。

私の標準的な説明は次のとおりです。あなたが10人のレストランを持つとします。ウェイターが来たら、彼が注文した最初の人は準備ができていません。しかし、他の9人はそうです。したがって、ウェイターは他の9人に注文を求め、元の人に戻ってくる。 (それは間違いなくではありません。元の人が注文する準備ができているのを待つために2番目のウェイターを手に入れてしまうと、おそらくあまり時間を節約できません)。これは、async/awaitが通常動作する方法です(Thread.Run(...)のようなTask Parallelライブラリ呼び出しのいくつかは実際にはです)は、が他のスレッドで実行されています - この例では、どのドキュメントがどのドキュメントであるかを確認してください。

コンソールアプリケーションで同期コンテキストを使用していない場合、どのスレッド非同期メソッドが実際に実行されるかについての保証はずっと少なくなるため、これで期待する動作を正確には行えない可能性があります場合。

本当にどちらが最終的に使用されるかは、タスクがCPUにバインドされているかどうかによって異なります。それは、CPUのバウンド操作ではない場合(つまり、サーバ、外部ハードウェアなどの結果を待っているだけです)、スレッドと非同期のパフォーマンスの差はあまり重要ではありません。ウェイターに「私に戻ってくる」と言っているのと同じです。ただし、CPUバインド操作の場合は、別のスレッドに配置することをお勧めします。

また、投稿したコードサンプルでは、​​すべてのケースで操作の結果を待つ理由がないようです。呼び出し元が結果をすぐに必要としない場合は、それを待つ必要はありません。実際、プロセスを待たずにプロセスを「蹴飛ばす」ことは、パフォーマンスを大幅に向上させることができます。もちろん、警告は、コンソールアプリケーションを閉じる前にすべてのタスクが完了していることを確認することです。

ほんの少しクリアしてください。そうでない場合は教えてください。私は自分の答えを編集できます。

関連する問題