0

ネットワーク内の 2 つのノード間でデータを渡すための Link クラスを作成しました。2 つの両端キュー (ノード 0 からノード 1 に向かうデータ用とノード 1 からノード 0 に向かうデータ用) を使用して実装しました。アプリケーションをマルチスレッド化しようとしていますが、スレッドロックが発生します。同じ両端キューからの読み取りと書き込みを同時に防止しようとしています。これを最初にどのように実装したかについて詳しく読むと、条件変数を間違って使用していると思います (ブール変数を使用するべきではないのでしょうか?)。各両端キューに 1 つずつ、合計 2 つのミューテックスが必要ですか? できれば助けてください。ありがとう!

class Link {
public:
// other stuff...
void push_back(int sourceNodeID, Data newData);
void get(int destinationNodeID, std::vector<Data> &linkData);

private:
// other stuff...
std::vector<int> nodeIDs_;
// qVector_ has two deques, one for Data from node 0 to node 1 and 
// one for Data from node 1 to node 0
std::vector<std::deque<Data> > qVector_; 
void initialize(int nodeID0, int nodeID1);

boost::mutex mutex_;
std::vector<boost::shared_ptr<boost::condition_variable> > readingCV_;
std::vector<boost::shared_ptr<boost::condition_variable> > writingCV_;
std::vector<bool> writingData_;
std::vector<bool> readingData_;
};

push_back 関数:

void Link::push_back(int sourceNodeID, Data newData)
{
int idx;
if (sourceNodeID == nodeIDs_[0]) idx = 1;
else 
{
    if (sourceNodeID == nodeIDs_[1]) idx = 0;
    else throw runtime_error("Link::push_back: Invalid node ID");
}

boost::unique_lock<boost::mutex> lock(mutex_);
// pause to avoid multithreading collisions
while (readingData_[idx]) readingCV_[idx]->wait(lock);

writingData_[idx] = true;
qVector_[idx].push_back(newData);
writingData_[idx] = false;
writingCV_[idx]->notify_all();
}

取得機能:

void Link::get(int destinationNodeID,
std::vector<Data> &linkData)
{
int idx;
if (destinationNodeID == nodeIDs_[0]) idx = 0;
else 
{
    if (destinationNodeID == nodeIDs_[1]) idx = 1;
    else throw runtime_error("Link::get: Invalid node ID");
}

boost::unique_lock<boost::mutex> lock(mutex_);
// pause to avoid multithreading collisions
while (writingData_[idx]) writingCV_[idx]->wait(lock);
readingData_[idx] = true;

std::copy(qVector_[idx].begin(),qVector_[idx].end(),back_inserter(linkData));
qVector_[idx].erase(qVector_[idx].begin(),qVector_[idx].end());
readingData_[idx] = false;
readingCV_[idx]->notify_all();
return;
}

ここで初期化します(役立つ場合に備えて)

void Link::initialize(int nodeID0, int nodeID1)
{
readingData_ = std::vector<bool>(2,false);
writingData_ = std::vector<bool>(2,false);
for (int i = 0; i < 2; ++i)
{
    readingCV_.push_back(make_shared<boost::condition_variable>());
    writingCV_.push_back(make_shared<boost::condition_variable>());
}
nodeIDs_.reserve(2);
nodeIDs_.push_back(nodeID0);
nodeIDs_.push_back(nodeID1);
qVector_.reserve(2);
qVector_.push_back(std::deque<Data>());
qVector_.push_back(std::deque<Data>());
}
4

2 に答える 2

2

アプリケーションをマルチスレッド化しようとしていますが、スレッドロックが発生します。

「スレッドロック」とは何ですか?コードが何を達成しようとしているのかを理解するのは困難です。最初に、同期された部分が次のような push_back() コードを検討してください。

boost::unique_lock<boost::mutex> lock(mutex_);

while (readingData_[idx]) readingCV_[idx]->wait(lock);

writingData_[idx] = true;
qVector_[idx].push_back(newData);
writingData_[idx] = false;
writingCV_[idx]->notify_all();

ブールwritingData[idx]値は false として始まり、スレッドがミューテックスをロックしている間、一瞬だけ true になります。ミューテックスが解放されるまでに、それは再び false になります。そのため、ミューテックスを取得するために待機する必要がある他のwritingData[idx]スレッドの場合、真になることはありません。

しかし、あなたの get() コードには、

boost::unique_lock<boost::mutex> lock(mutex_);
// pause to avoid multithreading collisions
while (writingData_[idx]) writingCV_[idx]->wait(lock);

スレッドがミューテックスのロックを取得するwritingData[idx]までに false に戻るため、while ループ (および CV の待機) に入ることはありません

厳密に対称的な分析がreadingData[idx]ブール値に適用され、mutex ロックの外側では常にfalse になります。

したがって、条件変数が待機されることはありません。設計を完全に再考する必要があります。

キューごとに 1 つのミューテックスから始めます (単純にデータを渡すには両端キューはやり過ぎです)。各キューについて、空でないキューに条件変数を関連付けます。したがって、get()メソッドはキューが空でなくなるまで待機し、push_back()メソッドで通知されます。このようなもの(テストされていないコード):

template <typename Data>
class BasicQueue
{
public:
    void push( Data const& data )
    {
        boost::unique_lock  _lock( mutex_ );
        queue_.push_back( data );
        not_empty_.notify_all();
    }

    void get ( Data& data )
    {
        boost::unique_lock  _lock( mutex_ );
        while ( queue_.size() == 0 )
            not_empty_.wait( _lock ); // this releases the mutex
        // mutex is reacquired here, with queue_.size() > 0
        data = queue_.front();
        queue_.pop_front();         
    }

private:
    std::queue<Data>            queue_;
    boost::mutex                mutex_;
    boost::condition_variable   not_empty_;
};
于 2013-01-13T07:49:26.357 に答える
1

はい。2 つのミューテックスが必要です。デッドロックは、ほぼ確実に単一のミューテックスでの競合の結果です。デバッガーを使用して実行中のプログラムに割り込むと、スレッドがハングしている場所が表示されます。また、ブール値が必要な理由もわかりません。(編集: 単一のミューテックスを使用する設計を考え出すことは可能かもしれませんが、共有データ構造ごとに 1 つのミューテックスを使用する方が簡単で安全です)

経験則では、保護しようとしている共有データ構造ごとに 1 つのミューテックスを持つことになります。そのミューテックスは、データ構造を同時アクセスから保護し、スレッド セーフを提供します。あなたの場合、両端キューごとに 1 つのミューテックス。例えば:

class SafeQueue
{
private:
  std::deque<Data> q_;
  boost::mutex m_;
  boost::condition_variable v_;

public:
  void push_back(Data newData)
  {
    boost::lock_guard<boost::mutex> lock(m_);
    q_.push_back(newData);
    // notify etc.
  }
// ...
};

条件変数による通知に関しては、こちらを参照してください。

生産者と消費者の状況での条件変数の使用

condition_variableそのため、プロデューサーが通知し、コンシューマーが待機するオブジェクトごとに 1 つも存在します。これで、双方向の通信用にこれらのキューを 2 つ作成できます。スレッドが 2 つしかなくても、両方のスレッドがブロックされ (データを待機)、両方のキューが空の場合、デッドロックが発生する可能性があることに注意してください。

于 2013-01-13T07:24:30.837 に答える