ユーザーから提供された作業を複数のスレッドに分散するクラスがあります。(簡略化)のようなもの:
class MT {
public:
MT();
void work1(/*args*/);
void work2(/*args*/);
void work3(/*args*/);
//waits until all threads complete, returns whether the work has been completed
//successfully, throws any exceptions that have been raised in the threads.
bool is_ok();
~MT();
private:
// thread pool
}
ユーザーはクラスを次のように使用します。
void foo()
{
MT mt;
mt.work1(/*userdata*/);
mt.work2(/*userdata*/);
mt.work1(/*userdata*/);
status = mt.is_ok();
if (status) {
mt.work3(/*userdata*/);
//...
}
//...
}
クラスがオブジェクトの一部になることはなく、常にスタックに格納されます。
問題
他のスレッドで作業しているときに発生した例外をなんとかして発行したいと思います。残念ながら、スレッドが正常に完了したかどうかは、スレッドに参加した後でしかわかりません。したがって、次の2つの選択肢から選択する必要があります。
のデストラクタのスレッドに参加し、
MT
作業中に発生した例外がある場合はそれをスローします。複数の例外がスローされた場合は、最初のタスクから発生した例外を選択してください。デストラクタがスタックの巻き戻しを行うように呼び出された場合(これは、を使用して確認できます。std::uncaught_exception
例外を飲み込んで防止しterminate()
ます。is_ok
常にデストラクタの前に呼び出すようにユーザーに指示します。
ユーザーは何も呼び出す必要がないので、最初のオプションはよりクリーンだと思います。ただし、デストラクタからのスローは通常、非常に強くお勧めしません。提示された議論は次のとおりです。
- デストラクタからのスローは危険です。これは、スタックの巻き戻し中に発生し、につながる可能性があるため
terminate()
です。 - 上記に対処したとしても、スタックが巻き戻されているかどうかに応じて投げるか投げないかは、動作の大幅な変更であり、推奨されません。
- 例外は、事後条件が満たされていないことを意味します。デストラクタの事後条件はリソースのクリーンアップです。これはどのような状況でも可能である必要があります。
どういうわけか、私は上記の議論はここでは当てはまらないと思う傾向があります:
- スタックの巻き戻しの問題を適切に処理します
- 使用パターンが原因で、終了する例外
foo
は、作業が失敗したことを意味します。デストラクタを投げるか投げないかは、動作の実質的な変更ではありません。 - デストラクタの事後条件は、リソースのクリーンアップだけでなく、作業が正常に完了したことでもあります。したがって、例外が適切であるはずです。
質問
- この場合、デストラクタからの投擲が適切だと思いますか?