2016-10-07 9 views
6

私はそのようなクラスではどうなるかと思った:マルチスレッド環境のデストラクタ?

class MyClass 
{ 
private: 
    std::vector<int> iVector; 
    void Worker() 
    { 
     //Lots of stuff done with iVector 
     //adding, removing elements, etc. 
    } 
} 

のは、私がiVectorを使用して、それを修正する(クラスのメンバ関数の一つによって呼び出される)スレッドを作成しましょう。この作業者以外に、このクラスの他のメンバ関数は、このstd :: vectorを読み取ったり変更したりしません。

iVectorを使用しているのはワーカースレッドだけなので、すべてうまく見えます。

しかし、オブジェクトの1つのインスタンスが破棄されるとどうなりますか?ワーカースレッドの終了後にオブジェクトが破壊されても、iVectorのデストラクタはメインスレッドから呼び出されます。これは未定義の動作につながりますか?

ありがとうございます!

+0

オブジェクトの存続期間がスレッドまたはオブジェクトの他の使用よりも長い場合、UBは存在しません。 –

+2

ちなみに、ベクトルがスレッドだけで使用され、他の場所で使用されていない場合は、そのスレッド内のローカル変数として使用してください。次に、このようなことについても心配する必要はありません。 –

+0

オブジェクトの存続期間は、もはやスレッドによって決まりません。しかし、それがスレッドB(ワーカースレッド)で変更されたときに、スレッドAのベクトルを破壊するのはなぜ素晴らしいですか? – sapito

答えて

3

まず、デストラクタのスレッドでstd::join(またはあなたのライブラリに相当するもの)を実行することをお勧めします。これにより、ベクトルデストラクタが実行される前にスレッドが正しく終了し、同期されるようになります。これは、ベクトルの寿命がそれを使用するスレッドを超えなければならないので重要です。

30.3.1.5でC++ 11標準及びおそらくそれ以降のもの状態:

5:同期この *によって表されるスレッドの完了は、対応する成功した(参加(1.10)と同期)return。 [ 注:* thisの操作は同期されません。 - 今は詳細については1.10を検討する必要がエンドノート]

、まずこのセクションの状態:

3:特定の点 におけるスレッドTに見えるオブジェクトの値でありますオブジェクトの初期値、 Tによってオブジェクトに割り当てられた値、または別のスレッドによってオブジェクトに割り当てられた値。 以下の規則に従います。

正直なところ、これは解析が難しく、どのような同期結合が提供されているのかを正確には指定せず、スレッド自体とアクセスしたデータだけを同期させることを暗示しているようです。したがって、私は安全なルートに行き、メインスレッドで結合した後にatomic_thread_fence(memory_order_acquire)を実行し、完了する直前に子スレッドのatomic_thread_fence(memory_order_release)を実行します。セマンティクスとUBの前に完全な状態が発生することを保証する必要があります。

+0

MyClassのデストラクタのスレッドでstd :: joinを呼び出しています。しかし、私は気づきませんでした:: std :: joinは、ワーカースレッドが行った変更が行方不明になるのを防ぎます:) – sapito

+0

@sapito私のC++標準を素早く読ませて、これが確実に安全かどうかを確認します。 – Vality

+0

@sapitoいくつかの標準的な引用符と、単独での参加が十分であると確信していない場合は、追加の安全性を達成する方法があります。正直なところ、結合がスレッドの終了自体、またはスレッドが使用していたデータだけを同期する場合は、標準から解析できません。どのような種類の同期意味論も提供していません。したがって、安全のためにフェンス経路を使用することができます。 – Vality

2

実行スレッドがivectorクラスメンバを使用していて、別のスレッドがこのクラスメンバでオブジェクトを破棄すると、ivectorクラスメンバが引き続き使用されると、未定義の動作が発生します。

ワーカースレッドの終了後にオブジェクトが破壊されても、iVector用のデストラクタである がメインスレッドから呼び出されます。 これは未定義の動作につながりますか?

いいえ、この状況は未定義の動作ではありません。 C++標準では、オブジェクトを作成した同じ実行スレッドによってオブジェクトが破棄される必要はありません。1つの実行スレッドが成長し、ベクトルのサイズを変更してから離れたり、ベクトルの使用をやめたりして、別の実行スレッドがオブジェクト全体を破壊することは問題ありません。

+0

私のオリジナルの質問の一部ではないとしても、デストラクタが(メンバー関数とは対照的に)他のスレッドによって加えられた変更とどのように同期するか知っているとよいでしょう。ところで、私が話しているのは、スレッドAはstd :: vectorを作成して破棄しますが(変更したり読むことはありません)、スレッドBはそれを変更して読み取ります。 – sapito

+0

この「同期」は、デストラクタを呼び出す行為とは関係ありません。 C++標準には、実行中のスレッドのすべての「変更」が別のスレッドで見えるようになる、「シーケンシング」の正式な仕様を完成させる退屈で長いセクションがあります。オブジェクトの破棄は、オブジェクトを破棄するスレッドが実際にそれを行う前に、他のスレッドからのオブジェクトへのすべての参照を順序付けする必要があるように順序付けされなければなりません。それ自身を適切に順序づけるのはマルチスレッドのコードに依存します。オブジェクトのデストラクタはその責任を負いません。 –

関連する問題