1. ホーム
  2. c++

C++11のスレッドセーフなキュー

2023-08-06 18:48:35

質問

私が取り組んでいるプロジェクトは、ファイルのコレクション上で作業を行うために複数のスレッドを使用します。各スレッドは処理されるファイルのリストにファイルを追加することができるので、私はスレッドセーフなキューを(私が考えたものを)一緒に置いた。関連する部分を以下に示します。

// qMutex is a std::mutex intended to guard the queue
// populatedNotifier is a std::condition_variable intended to
//                   notify waiting threads of a new item in the queue

void FileQueue::enqueue(std::string&& filename)
{
    std::lock_guard<std::mutex> lock(qMutex);
    q.push(std::move(filename));

    // Notify anyone waiting for additional files that more have arrived
    populatedNotifier.notify_one();
}

std::string FileQueue::dequeue(const std::chrono::milliseconds& timeout)
{
    std::unique_lock<std::mutex> lock(qMutex);
    if (q.empty()) {
        if (populatedNotifier.wait_for(lock, timeout) == std::cv_status::no_timeout) {
            std::string ret = q.front();
            q.pop();
            return ret;
        }
        else {
            return std::string();
        }
    }
    else {
        std::string ret = q.front();
        q.pop();
        return ret;
    }
}

しかし、たまに if (...wait_for(lock, timeout) == std::cv_status::no_timeout) { } ブロックの中でセグメンテーションフォールトが発生することがあります。これはどうしてなのでしょうか?私の理解では wait_for が返すのは cv_status::no_timeout を返すだけであり、これは FileQueue::enqueue が新しいアイテムをキューにプッシュした後でなければ発生しません。

どのように解決するのですか?

規格によると condition_variables は、たとえイベントが発生していなくても、 疑似的にウェイクアップすることが許されています。偽のウェイクアップが発生した場合、それは cv_status::no_timeout を返します (タイムアウトではなくウェイクアップしたため)。これに対する正しい解決策は、もちろん、ウェイクアップが実際に正当であったかどうかを手続きする前にチェックすることです。

詳細は標準の§30.5.1 [thread.condition.condvar]で指定されています。

-notify_one()の呼び出し、notify_all()の呼び出し、abs_timeで指定された絶対時間切れ(30.2.4)、またはスプリアスで合図されるとブロックを解除します。

...

を返します。 abs_time で指定された絶対タイムアウト(30.2.4)が経過した場合は cv_status::timeout ,それ以外は cv_status::no_timeout を返します.