オブジェクトからの状態変化に関する通知が、よく定義された順序でスロットに到着することを確認するために、boost :: signals2シグナルのマルチスレッド呼び出しをシリアル化したいと思います。boost :: signals2シグナルの呼び出しをシリアル化する既存の方法はありますか?
背景
私は、マルチスレッドプログラムで内部状態を持つオブジェクトを持っています。内部状態の一部は、プログラムの他の部分のために興味深いものです、そしてオブジェクトは、ブーストを使用して状態の変化を公開::これと同様の信号S2信号、:
class ObjectWithState {
public:
enum State {
STATE_A,
STATE_B,
STATE_C,
};
void OnEvent() {
State newState;
{
boost::lock_guard<boost::mutex> lock(m_Mutex);
// Process event and change state
m_State = ...;
newState = m_State;
}
m_OnStateChanged(newState);
}
// method to allow external objects to connect to the signal etc
private:
boost::signals2::signal<void (State) > m_OnStateChanged;
boost::mutex m_Mutex;
State m_State;
};
問題
あった場合OnEventハンドラの複数の同時呼び出しであるため、実際に行われた変更よりも別の順序で状態の変更が通知される可能性があります。状態そのものは上記のようなmutexによって保護されているので、の実際の状態は明確に定義されています。ただし、デッドロックの原因となる可能性があるため、ミューテックスはシグナルの呼び出しを介して保持することはできません。これは、信号の実際の呼び出しはどのような順序でも起こりうるが、ステートの変更が実際に行われたのと同じ順序で呼び出される必要があることを意味する。
この問題を処理する1つの方法は、信号から状態を削除し、状態が変更されたことをリスナーに通知することです。次に、彼らはそのオブジェクトの状態を問い合わせることができ、信号が発射されたときにオブジェクトが持っていた状態を得ることができる。またはそれ以降の状態。私のシナリオでは、リスナーはすべて状態の変更について通知する必要があるので、この方法はここでは機能しません。私は上記のコードを試していないので、それはバグやコンパイルエラーが散らばっされるかもしれないが、どのようなI推定することが可能でなければなりません
class ObjectWithState {
public:
enum State {
STATE_A,
STATE_B,
STATE_C,
};
void OnEvent() {
State newState;
boost::unique_future<void> waitForPrevious;
boost::shared_ptr<boost::promise<void> > releaseNext;
{
boost::lock_guard<boost::mutex> lock(m_Mutex);
// Process event and change state
m_State = ...;
newState = m_State;
waitForPrevious = m_CurrentInvocation->get_future();
m_CurrentInvocation.reset(new boost::promise<void>());
releaseNext = m_CurrentInvocation;
}
// Wait for all previous invocations of the signal to finish
waitForPrevious.get();
// Now it is our turn to invoke the signal
// TODO: use try-catch/scoped object to release next if an exception is thrown
OnStateChanged(newState);
// Allow the next state change to use the signal
releaseNext->set_value();
}
// method to allow external objects to connect to the signal etc
private:
boost::signals2::signal<void (State) > m_OnStateChanged;
boost::mutex m_Mutex;
State m_State;
// Initialized with a "fulfilled" promise in the constructor
// or do special handling of initially empty promise above
boost::shared_ptr<boost::promise<void> > m_CurrentInvocation;
};
:
私の次のアプローチは、次のようなものになるだろう後になる。私の腸の感覚は、私がこのタイプの問題に遭遇した最初の人ではないと私に伝えます。私は自分の試したコードを試してテストしたコードを使うほうが好きです... :)私の質問は本当にあります:
既存の方法がありますかboost :: signals2シグナルの直列化された呼び出し(signals2ライブラリまたは共通パターンに組み込まれたものなど)を実現するには?
"ただし、デッドロックにつながる可能性があるため、信号の呼び出しによってミューテックスを保持することはできません。あなたはこれを明確にすることができますか?シグナルハンドラの中には 'ObjectWithState'の状態を変更できるものがありますか?彼らは新しい 'OnEvent()'呼び出しを生成するでしょうか?無限回帰に入っていないことをどうやって確認しますか? – user1202136
@ user1202136:はい。再帰呼び出しはデッドロックを取得する1つの方法です。あなたが2つのObjectWithStateを持っていて、状態変更のハンドラを設定して、場合によっては他の状態の変更を引き起こす場合は、より卑劣な方法です。これは、最初のミューテックスを保持し、もう一方をロックしようとするスレッドと、もう一方のミューテックスを保持し、最初のミューテックスをロックしようとするスレッドにつながります。したがって、一般的なガイドラインは、未知のコードを呼び出している間に決してロックを保持することではありません。http://drdobbs.com/article/print?articleId=202802983&siteSectionName=を参照してください。 – villintehaspam