2

ネットワークスレッドが実行するすべてのフレームの最後に出力オブジェクトが作成されるマルチスレッドフレームワークを実装しようとしています。これにより、別のスレッドがそのフレームの開始時に最新の「完了した出力」ポインターを取得できます。また、出力オブジェクト内に保存されているデータへの安全で完全な読み取り専用アクセスがあることを知っています。

これに対する私の(非常に初期の)アプローチには、主に次のコードブロックが含まれます。

NetworkHandler -

void NetworkHandler::Tick()
    {
        // Tick on our io_service
        m_ios.poll();

        // Assemble new output data
        this->AssembleOutput();
    }

ClientNetworkHandler -

void ClientNetworkHandler::AssembleOutput()
    {
        std::tr1::shared_ptr<crusade::task::TaskOutput> newOutput(new crusade::task::TaskOutput());
        newOutput->m_outputElements["connected"] = std::tr1::shared_ptr<crusade::task::TaskOutputElement>(new NetworkConnectedTaskOutputElement(this->m_isConnected));
        this->m_latestOutput.swap(newOutput);
    }

PyCruHandler -

void PyCruHandler::Tick()
    {
        printf("PyCruHandler\n");
        // Get any necessary inputs from other threads
        m_latestNetworkOutput.swap(crusade::task::THManager::GetInstance()->GetTaskHandler(crusade::task::THManager::TH_NETWORK)->GetLatestOutput());
        // Other unrelated processing to go here
    }

基本的に、ClientNetworkHandler と PyCruHandler は別々のスレッドで独立して実行されます。PyCruHandler は m_latestNetworkOutput のコピーに対して実際には何もしません。何らかの方法でアクセスされる他のすべてのインスタンスをコメントアウトしましたが、まだ次の問題があります。

両方のスレッドが swap (または同等の operator=) を呼び出せるようにすると、最終的に (通常は実行から 2 秒以内ですが、数分かかることもあります)、オペレーターの新規またはアロケーターの削除のいずれかで次のエラーが発生します。選別:

「ヒープ: 解放された後に 2bab3dc で変更されたヒープ ブロック 2bab3b0 を解放する Windows がブレークポイントをトリガーしました。

これはヒープの破損が原因である可能性があり、これはバグを示しています...など."

私は初心者にすぎませんが、これは、shared_ptr オブジェクト間のスレッドセーフとタイミングに敏感なアクセスの問題に関するある種の問題を示しているようです。ただし、shared_ptr スレッド セーフのニュアンスの説明 (ここと他の場所の両方) に当惑するほど混乱してきました。参照カウントがスレッド セーフであることを示す読み取りがあるため、shared_ptrs をコピーしても安全です (ただし、その内部オブジェクト)。スレッド セーフにはなりません)、その他の読み物は、shared_ptr には有用なスレッド セーフが実質的に存在しないことを示しています。shared_ptrs のスレッド セーフに関するブースト ドキュメントを読みましたが、これが私のコードで問題になるかどうかはまだわかりませんでした。

私の質問は、ここにいる誰かが私がしていることの明らかな欠陥を見つけることができますか? 私の目標は、所有スレッドによってまだ保存されている最新の出力オブジェクトにアクセスできるようにすることです。その後、出力のすべてのユーザーがそれを処理するまで、そのオブジェクトは (所有スレッドが後の出力に移動した後でも) 削除されません。同じように。私の頭の中では、共有ポインターはこれに最適なように思えました...しかし、これに頭をぶつけて3時間後、私は疑問に思い始めています...

事前にどうもありがとうございました。これを誤って投稿した場合はお詫び申し上げます。ここに来るのは初めてで、プロトコルに関する限り、FAQ はかなりのんびりしているように見えました。

4

2 に答える 2

4

おそらくここでの最良のリソースはドキュメンテーションです:

スレッドセーフ

shared_ptr オブジェクトは、組み込み型と同じレベルのスレッド セーフを提供します。shared_ptr インスタンスは、複数のスレッドで同時に「読み取る」(const 操作のみを使用してアクセスする) ことができます。異なる shared_ptr インスタンスは、複数のスレッドによって同時に「書き込み」(operator= や reset などの変更可能な操作を使用してアクセス) できます (これらのインスタンスがコピーであり、その下で同じ参照カウントを共有している場合でも)。

例:

shared_ptr<int> p(new int(42));

//--- Example 1 ---

// thread A
shared_ptr<int> p2(p); // reads p

// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe

//--- Example 2 ---

// thread A
p.reset(new int(1912)); // writes p

// thread B
p2.reset(); // OK, writes p2

//--- Example 3 ---

// thread A
p = p3; // reads p3, writes p

// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write

//--- Example 4 ---

// thread A
p3 = p2; // reads p2, writes p3

// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"

//--- Example 5 ---

// thread A
p3.reset(new int(1));

// thread B
p3.reset(new int(2)); // undefined, multiple writes

私はm_latestOutputあなたのコードがshared_ptr- であると仮定します - その場合、あなたがヒットしている例は番号 5 (複数の書き込み) に最もよく似ています。

于 2009-07-19T23:28:02.343 に答える
1

ドキュメントによると、同時読み取りと書き込みで同期する必要があります。スワップは両方です:)。代わりに operator=() を使用することを検討してください。これは書き込みのみです。

また、クライアントスレッドは、完了するまでオブジェクトを存続させたい場合は、m_latestNetworkOutput (読み取られる) のコピーを作成する必要があります (それが PyCruHandler::Tick() で行っていることだと思います)。

とにかく、書き込みを同期する必要があります。

this->m_latestOutput.swap(newOutput);

そして読んでください:

m_latestNetworkOutput.swap(crusade::task::THManager::GetInstance()->GetTaskHandler(crusade::task::THManager::TH_NETWORK)->GetLatestOutput());

そして、スワップを代入に変更します。スワップ後に古​​いポインターは必要ありませんね。

于 2009-07-19T23:41:10.100 に答える