127

バックグラウンド スレッドで実行するタスクをトリガーしたいと考えています。タスクの完了を待ちたくありません。

.net 3.5 では、次のようにしました。

ThreadPool.QueueUserWorkItem(d => { DoSomething(); });

.net 4 では、TPL が推奨される方法です。私が推奨する一般的なパターンは次のとおりです。

Task.Factory.StartNew(() => { DoSomething(); });

ただし、StartNew()メソッドはTaskを実装するオブジェクトを返しますIDisposable。これは、このパターンを推奨する人々によって見落とされているようです。Task.Dispose()メソッドに関するMSDNのドキュメントには、次のように記載されています。

「タスクへの最後の参照を解放する前に、必ず Dispose を呼び出してください。」

タスクが完了するまで dispose を呼び出すことはできないため、メイン スレッドを待機させて dispose を呼び出すと、そもそもバックグラウンド スレッドで実行する意味がなくなります。また、クリーンアップに使用できる完了/終了イベントもないようです。

Task クラスの MSDN ページはこれについてコメントしておらず、本「Pro C#2010...」は同じパターンを推奨しており、タスクの破棄についてはコメントしていません。

そのままにしておくと、最終的にファイナライザーがそれをキャッチすることはわかっていますが、このような多くのファイアアンドフォーゲットタスクを実行していて、ファイナライザースレッドが圧倒されたときに、これが戻ってきて噛むことはありますか?

だから私の質問は:

  • Dispose()この場合、Taskクラスを呼び出さないことは許容されますか? もしそうなら、なぜ、そしてリスク/結果がありますか?
  • これについて説明しているドキュメントはありますか?
  • Taskまたは、私が見逃したオブジェクトを処分する適切な方法はありますか?
  • または、TPL を使用してファイア アンド フォーゲット タスクを実行する別の方法はありますか?
4

3 に答える 3

114

これについては、MSDN フォーラムで議論されています。

Microsoft pfx チームのメンバーである Stephen Toub は、次のように述べています。

Task.Dispose が存在するのは、Task が、タスクの完了を待機するときに使用されるイベント ハンドルをラップする可能性があるためです。これは、待機中のスレッドが実際にブロックする必要がある場合 (待機中のタスクをスピンまたは潜在的に実行するのではなく) です。継続を使用しているだけの場合、そのイベント ハンドルは決して割り当てられません

物事を処理するためにファイナライズに依存する方がよいでしょう。

更新 (2012 年 10 月) Stephen Toub はDo I need to dispose of Tasks?
というタイトルのブログを投稿しました。これにより、さらに詳細が得られ、.Net 4.5 の改善点が説明されます。

Task要約すると、 99% の確率でオブジェクトを破棄する必要はありません。

オブジェクトを破棄する主な理由は 2 つあります。アンマネージ リソースをタイムリーかつ決定論的な方法で解放することと、オブジェクトのファイナライザーを実行するコストを回避することです。Taskほとんどの場合、これらのいずれにも当てはまりません。

  1. .Net 4.5 の時点で、が内部待機ハンドル (オブジェクトTask内の唯一のアンマネージ リソース) を割り当てるのは、のを明示的に使用するときだけです。TaskIAsyncResult.AsyncWaitHandleTask
  2. オブジェクト自体にはTaskファイナライザーがありません。ハンドル自体は、ファイナライザーを使用してオブジェクトにラップされているため、割り当てられていない限り、実行するファイナライザーはありません。
于 2010-09-17T09:54:32.763 に答える
13

これは Thread クラスと同じ種類の問題です。5 つのオペレーティング システム ハンドルを使用しますが、IDisposable を実装していません。元の設計者の適切な判断です。もちろん、Dispose() メソッドを呼び出す合理的な方法はほとんどありません。最初に Join() を呼び出す必要があります。

Task クラスは、これに 1 つのハンドル (内部手動リセット イベント) を追加します。最も安価なオペレーティング システム リソースはどれですか。もちろん、その Dispose() メソッドは、Thread が消費する 5 つのハンドルではなく、その 1 つのイベント ハンドルしか解放できません。 ええ、気にしないでください

タスクの IsFaulted プロパティに関心を持つ必要があることに注意してください。これはかなり厄介なトピックです。詳細については、このMSDN ライブラリの記事 を参照してください。これを適切に処理したら、タスクを破棄するための適切な場所がコード内にあるはずです。

于 2010-09-17T13:02:09.533 に答える
-1

この投稿で示されている手法について誰かが検討するのを楽しみにしています: C# での Typesafe fire-and-forget 非同期デリゲート呼び出し

単純な拡張メソッドは、タスクと対話するすべての些細なケースを処理し、それに対して dispose を呼び出すことができるように見えます。

public static void FireAndForget<T>(this Action<T> act,T arg1)
{
    var tsk = Task.Factory.StartNew( ()=> act(arg1),
                                     TaskCreationOptions.LongRunning);
    tsk.ContinueWith(cnt => cnt.Dispose());
}
于 2010-12-09T14:20:13.243 に答える