バックグラウンドスレッドで実行するタスクをトリガーしたい。タスクの完了を待ちたくありません。
.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 ...」という本は同じパターンを推奨しており、タスクの廃棄についてはコメントしていません。
ファイナライザが最後にそれを捕まえるかどうかを知っていますが、たくさんの火をやり、このようなタスクを忘れて、ファイナライザのスレッドが圧倒されると、これが戻ってきて噛みつきますか?
だから私の質問は:
Task
クラスでDispose()
を呼び出さないことは許容されますか?もしそうなら、なぜ、リスク/結果がありますか?Task
オブジェクトを廃棄する適切な方法はありますか?これについての議論があります MSDNフォーラムで 。
Microsoft pfxチームのメンバーであるStephen Toubは次のように述べています。
Task.Disposeは、タスクが完了するのを待機しているときに使用されるイベントハンドルを潜在的にラップするために、待機スレッドが実際にブロックする必要がある場合に存在します(スピンまたは待機中のタスクの実行とは対照的に)。継続を使用している場合、そのイベントハンドルは割り当てられません
...
物事の世話をするには、ファイナライズに頼る方が良いでしょう。
更新(2012年10月)
Stephen Toubが タスクを破棄する必要がありますか? というタイトルのブログを投稿しました。このブログでは、詳細を説明し、.Net 4.5の改善点について説明しています。
要約すると、Task
オブジェクトを99%廃棄する必要はありません。
オブジェクトを破棄する主な理由は2つあります。タイムリーかつ決定的な方法でアンマネージリソースを解放することと、オブジェクトのファイナライザを実行するコストを回避することです。ほとんどの場合、これらのいずれもTask
には適用されません。
Task
が内部待機ハンドル(Task
オブジェクト内の唯一のアンマネージリソース)を割り当てるのは、Task
のIAsyncResult.AsyncWaitHandle
を明示的に使用するときだけです。Task
オブジェクト自体にはファイナライザーがありません。ハンドル自体はファイナライザでオブジェクトにラップされるため、割り当てられない限り、実行するファイナライザはありません。これは、Threadクラスの場合と同じ種類の問題です。 5つのオペレーティングシステムハンドルを消費しますが、IDisposableを実装しません。元の設計者の良い決断です。もちろん、Dispose()メソッドを呼び出す合理的な方法はほとんどありません。最初にJoin()を呼び出す必要があります。
Taskクラスは、これに1つのハンドル、内部手動リセットイベントを追加します。最も安価なオペレーティングシステムリソースがあります。もちろん、そのDispose()メソッドは、スレッドが消費する5つのハンドルではなく、1つのイベントハンドルのみをリリースできます。 うん、気にしないでください 。
タスクのIsFaultedプロパティに興味があることに注意してください。これはかなりいトピックです。詳細については、この MSDNライブラリの記事 を参照してください。これを適切に処理すれば、タスクを破棄するための適切な場所もコードに確保する必要があります。