1. ホーム
  2. c#

[解決済み] TPLタスクオブジェクトでDispose()を呼び出さないことは許容されると考えられますか?

2022-07-29 14:37:30

質問

バックグラウンドスレッドでタスクを実行させたいと思います。私はタスクの完了を待ちたくありません。

.net 3.5では、私はこれを行いました。

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

.net 4では、TPLが推奨される方法です。私が見たことのある一般的なパターンは、推奨されています。

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

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

"タスクへの最後の参照を解放する前に、常にDisposeを呼び出します。

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

MSDN の Task クラスのページにはこれに関するコメントがなく、書籍 "Pro C#2010..." にも同じパターンが推奨されており、タスクの破棄に関するコメントはありません。

このまま放置しておけば最終的にファイナライザがキャッチしてくれるのは分かっているのですが、このようなfire & forgetタスクをたくさんやっていてファイナライザのスレッドが圧迫されたときに、これは返ってくるのでしょうか?

そこで質問です。

  • を呼び出さないことは許容されるのでしょうか? Dispose() の上で Task クラスで使用できますか?そして、もしそうなら、その理由とリスク/結果はあるのでしょうか?
  • このことについて説明しているドキュメントはありますか?
  • それとも Task オブジェクトを破棄する適切な方法があるのでしょうか?
  • あるいは、TPL で fire & forget タスクを行う別の方法がありますか。

どのように解決するのですか?

この件に関する議論があります について、MSDN フォーラムで .

Microsoft pfx チームのメンバーである Stephen Toub はこのように言っています。

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

...

ということで、ファイナライズに頼るほうがよさそうです。

更新情報(2012年10月)

Stephen Toubは、以下のタイトルのブログを投稿しています。 タスクの破棄は必要ですか? というタイトルのブログを投稿し、いくつかの詳細と .Net 4.5 での改善点を説明しています。

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

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

  1. .Net 4.5時点では、唯一 Task が内部待機ハンドル(の中で唯一管理されていないリソース)を 割り当てるときだけです。 TaskIAsyncResult.AsyncWaitHandle であり
  2. Task オブジェクト自体はファイナライザを持ちません。ハンドルはそれ自体がファイナライザを持つオブジェクトにラップされているので、それが割り当てられない限り、実行すべきファイナライザはありません。