2

ユーザーから提供された作業を複数のスレッドに分散するクラスがあります。(簡略化)のようなもの:

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は、作業が失敗したことを意味します。デストラクタを投げるか投げないかは、動作の実質的な変更ではありません。
  • デストラクタの事後条件は、リソースのクリーンアップだけでなく、作業が正常に完了したことでもあります。したがって、例外が適切であるはずです。

質問

  • この場合、デストラクタからの投擲が適切だと思いますか?
4

1 に答える 1

7

いいえ、デストラクタからスローしないでください。呼び出しis_ok、キャッチし、無視します。close()ストリームの場合と同じです。

ユーザーはis_ok 、作業が完了したことを確認したい場合に電話をかけることができ、失敗すると例外がスローされます。

実際には、これが不便になることはめったにありません。ユーザーが複数のリターンポイントを持つ関数を作成する場合ですが、(残念ながら)C ++はユーザーにそれを実行する手段を提供しないため、これはユーザーが対処しなければならない問題です。std::threadこれが反社会的であると思われる場合は、C ++ 11で、最初に結合または分離せずにを破壊するとどうなるかを見てください。あなたはそれを打ち負かしています:-)

したがって、ユーザーはis_okすべての非エラー出口パスを呼び出します。とにかく別の例外を処理できないため、ユーザーは、すでに例外がある場合にわざわざ電話をかける必要はありません。繰り返しますが、これはclose()ストリームの場合と同じです。バッファリングされたストリームの書き込みによるエラーを確認したい場合は、明示的に閉じるかフラッシュする必要があります。

上記に対処したとしても、スタックが巻き戻されているかどうかに応じて投げるか投げないかは、動作の大幅な変更であり、推奨されません。

また、少なくともC ++ 03では、正しく実行することはできません。C ++ 11がこの点で何かを変えるかどうかはわかりませstd::uncaught_exception んが、ハーブサッターが説明しているように、あなたが知る必要があることはわかりません。

cppreference.comへのリンクから:

std :: uncaught_exception()==trueの間にスローされた例外はstd::terminateを呼び出します。

これは間違いだと私は確信しています。terminateスタックアンワインドの一部として呼び出されるデストラクタを例外がエスケープした場合に呼び出されますが、アンワインド中にデストラクタが例外をスローしてキャッチしたという理由だけで呼び出されるわけではありません。

呼び出し元は、デストラクタでスローされた例外を除いて何か便利なことができると考えた場合、そのデストラクタを呼び出すヘルパークラスを作成し、それをis_okオブジェクト配置できます。クラスが同じ例外で実行できる何か便利なことがあると思う場合は、デストラクタでそれを単に無視する以外のことを実行できますが、それでもデストラクタを離れることを許可しないでください。

于 2012-10-19T12:00:46.097 に答える