2017-01-31 10 views
3

に多くのタスクを実行する場合:C#のスレッドを単一のスレッド上で、私はこれを行う、場合どうなりますかTask.WhenAll

await Task.WhenAll(items.select(x => SomeAsyncMethod(x))) 

// Where SomeAsyncMethod is defined like this (writeAsync is pure async io) 
async Task SomeAsyncMethod(Item item){ 
    await myDevice.writeAsync(...).ConfigureAwait(false); 
    //do some cpu intensive stuff... 
} 

をしてitemsで10.000の項目があると言います。 SomeAsyncMethodのそれぞれが待ってから続けると、スレッドプールのスレッド上で実行されます。だからSomeAsyncsMethodの多くが返ってくると、スレッドプールからの複数のスレッドが同時に取得されるか、この場合にはいつでも1つのスレッドだけがSomeAsyncMethodに "do some CPU intensive stuff"を実行しますか?

更新:ここにサンプルプログラムがあります。 8つの論理コアを持つPCでこれをテストすると、minthreadsは12または13で、maxthreadsは35-40の範囲で終了します。 したがって、最大4つのスレッドが論理コアとして作成されるように見えます。 10.000または100.000ファイルが作成されても問題はありません。同じ最大スレッド数が使用されています。これは、すべてのタスクがファイルシステムへのアクセスを待っているためですか?このプログラムは、Cに小さなファイルの多くを作成することに、注意してください:\ TMP \はasynctest:今、私はそれぞれ同時にaysnc IOタスクの多くを行う複数のスレッドを起動した場合

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApplication4 { 
    internal class Program { 
     private static void Main(string[] args) { 
      var myDevice = new MyDevice(); 
      var ints = new List<int>(); 
      for (var i = 0; i < 10000; i++) { 
       ints.Add(i); 
      } 
      var task = Task.WhenAll(ints.Select(i => myDevice.WriteTextAsync(i.ToString()))); 
      task.Wait(); 
      Console.WriteLine("Max thread count = " + myDevice.MaxThreadCount); 
      Console.WriteLine("Min thread count = " + myDevice.MinThreadCount); 
      Console.ReadLine(); 
     } 
    } 

    public class MyDevice { 
     public ConcurrentDictionary<string, string> ThreadIds; 
     public int MaxThreadCount; 
     public int MinThreadCount = Process.GetCurrentProcess().Threads.Count; 
     public async Task WriteTextAsync(string text) { 
      var filePath = @"c:\tmp\asynctest\" + text + ".txt"; 
      var encodedText = Encoding.Unicode.GetBytes(text); 
      using (var sourceStream = new FileStream(filePath, 
       FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) { 
       await sourceStream.WriteAsync(encodedText, 0, encodedText.Length).ConfigureAwait(false); 
       MaxThreadCount = Math.Max(MaxThreadCount, Process.GetCurrentProcess().Threads.Count); 
       MinThreadCount = Math.Min(MinThreadCount, Process.GetCurrentProcess().Threads.Count); 
      } 
     } 
    } 
} 

更新2.、それはしていません私が実行したばかりのテストでは、10.000個のファイルがそれぞれ4つのスレッドで作成された後、最大スレッドは41、最小スレッドは12だったようです。非同期タスクの継続に使用されるスレッドの数を集中管理することができます。ここでは4つのスレッドが10.000非同期操作のそれぞれを開始します例です。

using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApplication4 { 
    internal class Program { 
     private static void Main(string[] args) { 
      var myDevice = new MyDevice(); 
      var ints = new List<int>(); 
      const int limit = 10000; 
      for (var i = 0; i < limit; i++) { 
       ints.Add(i); 
      } 

      List<Task> jobs = new List<Task>(); 
      for (var j = 0; j < 4*limit; j+=limit) { 
       var jobid = j; 
       jobs.Add(Task.Run(() => Runjob(ints, myDevice, jobid))); 
      } 
      Task.WaitAll(jobs.ToArray()); 

      Console.WriteLine("Max thread count = " + myDevice.MaxThreadCount); 
      Console.WriteLine("Min thread count = " + myDevice.MinThreadCount); 
      Console.ReadLine(); 
     } 

     private static void Runjob(List<int> ints, MyDevice myDevice, int jobid) { 
      Console.WriteLine("Starting job " + jobid); 
      var task = Task.WhenAll(ints.Select(i => myDevice.WriteTextAsync((jobid+i).ToString()))); 
      task.Wait(); 
      Console.WriteLine("Finished job " + jobid); 
     } 
    } 

    public class MyDevice { 
     public int MaxThreadCount; 
     public int MinThreadCount = Process.GetCurrentProcess().Threads.Count; 
     public async Task WriteTextAsync(string text) { 
      var filePath = @"c:\tmp\asynctest\" + text + ".txt"; 
      var encodedText = Encoding.Unicode.GetBytes(text); 
      using (var sourceStream = new FileStream(filePath, 
       FileMode.Append, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) { 
       await sourceStream.WriteAsync(encodedText, 0, encodedText.Length).ConfigureAwait(false); 
       MaxThreadCount = Math.Max(MaxThreadCount, Process.GetCurrentProcess().Threads.Count); 
       MinThreadCount = Math.Min(MinThreadCount, Process.GetCurrentProcess().Threads.Count); 
      } 
     } 
    } 
} 
+0

すべてに依存します。 SynchronisationContext。 –

+0

最も簡単な方法は、試してみることです。 Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);を追加して見つけてください。 –

+0

さて、私はConfigureAwaitをfalseにして、コンテキストが必要ではないと言うことができます(私はこれを再考するために今質問を編集します)。 –

答えて

3

最も可能性の高いシナリオは、「CPU instensiveものが」それぞれランダムスレッドプールのスレッドで起こるだろうということである - それは本当にCPU-だ場合と、論理的なコアごとに約1〜2つのスレッドが得られます。

キーポイントは、(もちろん、同期コンテキストが場合)元のタスク(Task.WhenAll)に続き、個々のI/O操作に、バックUIスレッド上で継続を実行する一方ことである意志(ConfigureAwait(false))、同期コンテキストを明示的に無視するように要求しているため、スレッドプールに投稿する必要があります。

しかし、I/O要求が同期して完了すると、すべてのスレッドが元のスレッドで実行される可能性もあります。この場合、非同期ディスパッチは行われず、スレッドがスレッドを切り替える機会はありません。並列化を確実にする必要がある場合は、明示的にTask.Runを使用する必要があります。

これは主に実装に依存していて、依存できるものではないことにも注意してください。また、I/OスレッドプールからスレッドにCPUを集中的に使用してスレッドプール内のスレッドのフレームワークのバランスを崩すことになるため、非常に非同期のI/Oアプリケーションにとっては悪いアプローチかもしれませんが、作業を終了するまで、新しい非同期的な応答が来ないようにします。これは特に、の作業がの純粋なCPU作業ではない場合に特に当てはまります。たとえば、スレッドプールスレッドのブロックは、Webサーバーのようなものでは非常に苦労する可能性があります。

+0

ありがとうございました。興味深いのは、1-2スレッドpr。コアは作業を行います。つまり、1つのスレッドから非同期IOコードだけを実行しても、マルチスレッド実行を終了することができます。知っておいてよかったです。 –

+0

元の質問に対する私の更新は、それが論理コアあたり3つのスレッドに似ていることを示しています。 –

+0

@StigSchmidtNielssonもちろん、同期コンテキストがない場合は、どのように実行を同期しますか?これが同期化のコンテキストです。単一スレッドのアプリケーションを設計するのに時間が掛かったのに少し奇妙ですが、同期は特別なケースです。同期の境界を明示的に指定していない場合は、何がどのような順序で起こるかについては想定しないでください。継続を単一の実行スレッドにマーシャリングする場合は、同期コンテキストを使用します。これにより、効果的にシングルスレッド非同期コードが提供されます。 – Luaan

関連する問題