3

TPL がタスク指向であるのに対し、従来のスレッド モデルはワーカー指向であることはわかっています。タスクを使用すると、解決方法の仕組みではなく、解決したい問題に主に集中できます。しかし、スレッドとタスクの関係に関しては、まだ少し混乱しています。

以下はデモコードです。

namespace AsyncUnderTheHood
{
    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("Main Start : {0}", Thread.CurrentThread.ManagedThreadId);
            AwaitTest();
            Console.WriteLine("Main End : {0}", Thread.CurrentThread.ManagedThreadId);
            Console.ReadLine();
        }

        public static void DoWork()
        {
            Console.WriteLine("DoWork Start: {0}", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(5000);
            Console.WriteLine("DoWork End: {0}", Thread.CurrentThread.ManagedThreadId);
        }

        public async static void AwaitTest()
        {
            Console.WriteLine("AwaitTest Start : {0}", Thread.CurrentThread.ManagedThreadId);
            Task t = new Task(DoWork);
            t.Start();
            await t;
            Console.WriteLine("AwaitTest Done : {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }
}

出力は次のようになります。

Main Start : 1
AwaitTest Start : 1   <------------ A
DoWork Start: 3
Main End : 1
DoWork End: 3
AwaitTest Done : 3    <------------ B

私の質問は、なぜ A と B が別のスレッドにあるのですか?

同じメソッドが異なるスレッドで実行されますが、スレッド アフィニティが重要な場合に問題が発生しますか?

4

3 に答える 3

3

なぜAとBは別のスレッドにあるのですか?

まず、Taskがデフォルトのスケジューラによってスケジュールされている場合、どれが実行されるかは保証されませThreadTask。また、 の部分はAwaitTest()個別に実行されるため、同じスレッドで実行される保証はありません。

次に、デフォルトのスケジューラは を使用してThreadPoolを実行しTaskます。そして、各asyncメソッドの最初の部分は同期的に実行されます。あなたの場合、これはの最初の部分がAwaitTest()メインスレッドで実行され、2番目の部分がいくつかのThreadPoolスレッドで実行されることを意味します。したがって、それらが同じスレッドで実行されないことが実際に保証されます。

スレッド アフィニティが重要な場合、これにより問題が発生しますか?

確かにそうかもしれません。ただし、スレッド アフィニティが重要な最も一般的なケースである GUI プログラミングでは正しく機能します。これは、GUI アプリケーションがSynchronizationContext設定されているためです。つまり、asyncメソッドの最初の部分が UI スレッドで実行される場合、2 番目の部分もそこで実行されます (を使用してこれを無効にしない限りConfigureAwait(false))。

しかし、それ以外の場合は、問題が発生します。たとえば、次のコードを見てください。

Monitor.Enter(lockObject);
await someTask;
Monitor.Exit(lockObject);

は とは異なるスレッドで実行できるため、このコードはコンソール アプリケーションでは機能しません (Exit()ほとんどの場合、 がスローされます) 。SynchronizationLockExceptionExit()Enter()

于 2013-04-07T16:40:19.343 に答える
1

システムにタスクを「待機」するように依頼しました。あなたが本当に求めているのは、呼び出されたスレッドawaitが実行を継続し、その後のすべてawaitがタスクの完了時に非同期で実行される「継続」であることです。コンソール アプリケーションには「メッセージ ポンプ」がないため、「メイン」スレッドにマーシャリングして戻す簡単な方法がなく、非同期のスレッドで継続が続行されTaskます。WinForm または WPF アプリケーションで同じテストを実行した場合、継続は UI スレッドで実行されます。

于 2013-04-07T16:35:02.937 に答える
0

このブログ投稿では、TPL が複数のタスク キューをワーク スティーリングと共に使用して、最適なパフォーマンスを得るために既存のスレッド プールの上に重ねる方法について詳しく説明しています。

于 2013-04-07T18:09:21.797 に答える