19

非同期CTPを評価しています。

別のスレッドプールのスレッドで非同期関数の実行を開始するにはどうすればよいですか?

static async Task Test()
{
    // Do something, await something
}

static void Main( string[] args )
{
    // Is there more elegant way to write the line below?
    var t = TaskEx.Run( () => Test().Wait() );

    // Doing much more in this same thread
    t.Wait(); // Waiting for much more then just this single task, this is just an example
}
4

3 に答える 3

51

私はStackOverflowを初めて使用しますが、Microsoftで作業しているチームに所属しているので、非同期CTPについて質問されていることに驚いています:)

私はあなたが何を目指しているのか理解していると思います、そしてあなたがそこに到達するためにあなたが正しくやっていることがいくつかあります。

私があなたが望むと思うもの:

static async Task Test()
{
    // Do something, await something
}

static void Main(string[] args)
{
    // In the CTP, use Task.RunEx(...) to run an Async Method or Async Lambda
    // on the .NET thread pool
    var t = TaskEx.RunEx(Test);
    // the above was just shorthand for
    var t = TaskEx.RunEx(new Func<Task>(Test));
    // because the C# auto-wraps methods into delegates for you.

    // Doing much more in this same thread
    t.Wait(); // Waiting for much more then just this single task, this is just an example
}

Task.RunとTask.RunEx

このCTPは.NET4.0の上にインストールされるため、mscorlibの実際のタイプにパッチを適用する必要はありませんでした。 System.Threading.Tasks.Task代わりに、プレイグラウンドAPIは、競合する場合はFooExという名前になります。

なぜそれらRun(...)のいくつかといくつかに名前を付けたのRunEx(...)ですか?その理由は、CTPをリリースするまでにまだ完了していなかったメソッドのオーバーロードの再設計によるものです。現在動作しているコードベースでは、C#メソッドのオーバーロードルールを微調整して、非同期ラムダに対して正しいことが行われるようにする必要がありました。これにより、、、、またはが返される可能性がvoidありTaskますTask<T>

問題は、非同期メソッドまたはラムダがTaskまたはTask<T>を返す場合、タスクはメソッドまたはラムダの呼び出しの一部として自動的に生成されるため、実際にはreturn式に外部タスクタイプがないことです。通常、returnステートメントの式はメソッドまたはラムダのreturn型に直接変換できるため、これはコードを明確にするための適切なエクスペリエンスを強く望んでいますが、以前はまったく異なっていました。

したがって、非同期voidラムダと非同期Taskラムダの両方が引数なしでサポートreturn;されます。したがって、どちらを選択するかを決定するために、メソッドのオーバーロード解決を明確にする必要があります。したがって、Run(...)とRunEx(...)の両方を使用する唯一の理由は、PDC 2010がヒットするまでに、非同期CTPの他の部分をより高品質でサポートできるようにするためです。


非同期メソッド/ラムダについて考える方法

これが混乱のポイントであるかどうかはわかりませんが、私はそれについて言及したいと思いました-非同期メソッドまたは非同期ラムダを書いているとき、それはそれを呼び出す人の特定の特性を帯びることがあります。これは、次の2つのことを前提としています。

  • あなたが待っているタイプ
  • そしておそらく同期コンテキスト(上記に応じて)

待機用のCTP設計と現在の内部設計はどちらも非常にパターンベースであるため、APIプロバイダーは、「待機」できる活気に満ちた一連の機能を具体化するのに役立ちます。これは、待機しているタイプによって異なる可能性があり、その一般的なタイプはですTask

Taskの待機の実装は非常に合理的であり、現在のスレッドに依存してSynchronizationContext作業を延期する方法を決定します。すでにWinFormsまたはWPFメッセージループに入っている場合、延期された実行は同じメッセージループに戻ります(BeginInvoke()「メソッドの残りの部分」を使用したかのように)。タスクを待っていて、すでに.NETスレッドプールを使用している場合、「メソッドの残りの部分」はスレッドプールスレッドの1つで再開されます(ただし、必ずしも同じスレッドである必要はありません)。ほとんどの場合、最初に利用可能なプールスレッドを使用して満足しています。


Wait()メソッドの使用に関する注意

使用したサンプルでは、​​次のようになります。 var t = TaskEx.Run( () => Test().Wait() );

それは何ですか:

  1. 周囲のスレッドで、TaskEx.Run(...)を同期的に呼び出して、スレッドプールでラムダを実行します。
  2. ラムダにはスレッドプールスレッドが指定されており、非同期メソッドを呼び出します。
  3. 非同期メソッドTest()はラムダから呼び出されます。ラムダはスレッドプールで実行されていたため、Test()内の継続は、スレッドプール内の任意のスレッドで実行できます。
  4. ラムダは、待機がなかったため、実際にはそのスレッドのスタックを空にしません。この場合のTPLの動作は、Wait()呼び出しの前にTest()が実際に終了したかどうかによって異なります。ただし、この場合、Test()が別のスレッドで実行を終了するのを待機している間、スレッドプールスレッドをブロックする可能性があります。

これが「await」演算子の主な利点です。元のスレッドをブロックすることなく、後で実行するコードを追加できることです。スレッドプールの場合、スレッドの使用率を向上させることができます。

VBまたはC#の非同期CTPについて他に質問がある場合は、お知らせください。ぜひお聞かせください:)

于 2011-02-12T13:39:10.010 に答える
5

Task他の何かに便乗するのではなく、真に新しい作業を開始する場合は、通常、を返すメソッドが実行場所を決定します。

この場合、メソッドを本当に非同期にしたいようには見えませんTest()。少なくとも、非同期であるという事実を使用していません。別のスレッドで作業を開始しているだけです...Test()メソッドは完全に同期している可能性があり、次のように使用できます。

Task task = TaskEx.Run(Test);
// Do stuff
t.Wait();

これには、非同期CTPの良さは必要ありません。

于 2011-02-11T20:01:40.470 に答える
2

これがコンソールアプリケーションでなければ、あるでしょう。たとえば、Windowsフォームアプリケーションでこれを行う場合、次のことができます。

// Added to a button click event, for example
public async void button1_Click(object sender, EventArgs e)
{
    // Do some stuff
    await Test();
    // Do some more stuff
}

ただし、コンソールにはデフォルトがSynchronizationContextないため、期待どおりに機能しません。コンソールアプリケーションでは、タスクを明示的に取得して、最後に待機する必要があります。

これをWindowsフォーム、WPF、またはWCFサービスのUIスレッドで実行している場合は、結果を適切にマーシャリングするために使用される有効なSynchronizationContextがあります。ただし、コンソールアプリケーションでは、await呼び出し時に制御が「返される」と、プログラムは続行され、すぐに終了します。これはすべてを台無しにし、予期しない動作を引き起こす傾向があります。

于 2011-02-11T19:58:16.227 に答える