std :: conditional_variable :: notify_all不会唤醒所有线程

时间:2016-02-11 19:28:56

标签: c++ multithreading c++11 condition-variable thread-synchronization

我在这里有一个简单的例子:

自从我尝试学习c ++ 11线程以来,这个项目可以被称为学术。 以下是对正在发生的事情的描述。

想象一个非常大的std::string里面有许多汇编源代码,比如

  

mov ebx,ecx; \ r \ nmov eax,ecx; \ r \ n .......

Parse()函数接受此字符串并通过标记行的开头和结尾并将这些行保存为string::const_iterators来查找所有行位置。

之后,2个工作线程从队列中弹出此信息,并将子字符串解析为Intstuction类对象。他们将结果的Instruction类实例推送到std::vector<Instruction> result

这是一个结构声明,用于保存行号以及要解析的子字符串的迭代器

struct JobItem {
    int lineNumber;
    string::const_iterator itStart;
    string::const_iterator itEnd;
};

那是一个小记录器......

void ThreadLog(const char* log) {
    writeMutex.lock();
    cout << "Thr:" << this_thread::get_id() << " " << log << endl;
    writeMutex.unlock();
}

这是共享数据:

queue<JobItem> que;
vector<Instruction> result;

以下是同步的所有原语

condition_variable condVar;
mutex condMutex;
bool signaled = false;

mutex writeMutex;
bool done=false;
mutex resultMutex;
mutex queMutex;

每线程功能

void Func() {
    unique_lock<mutex> condLock(condMutex);
    ThreadLog("Waiting...");
    while (!signaled) {
        condVar.wait(condLock);
    }
    ThreadLog("Started");
    while (!done) {
        JobItem item;
        queMutex.lock();
        if (!que.empty()) {
            item = que.front(); que.pop();
            queMutex.unlock();
        }
        else {
            queMutex.unlock();
            break;
        }
        //if i comment the line below both threads wake up
        auto instr = ParseInstruction(item.itStart, item.itEnd);
        resultMutex.lock();
        result.push_back(Instruction());
        resultMutex.unlock();
    }

管理线程的管理器功能......

vector<Instruction> Parser::Parse(const string& instructionStream){
    thread thread1(Func);
    thread thread2(Func);

    auto it0 = instructionStream.cbegin();
    auto it1 = it0;
    int currentIndex = instructionStream.find("\r\n");
    int oldIndex = 0;
    this_thread::sleep_for(chrono::milliseconds(1000)); //experimental 


    int x = 0;
    while (currentIndex != string::npos){
        auto it0  = instructionStream.cbegin() + oldIndex;
        auto it1  = instructionStream.cbegin() + currentIndex;

        queMutex.lock();
        que.push({ x,it0,it1 });
        queMutex.unlock();
        if (x == 20) {//fill the buffer a little bit before signal
            signaled = true;
            condVar.notify_all();
        }
        oldIndex = currentIndex + 2;
        currentIndex = instructionStream.find("\r\n", oldIndex);
        ++x;
    }
    thread1.join();
    thread2.join();
    done = true;

    return result;
}

问题出现在Func()函数中。如您所见,我在其中使用了一些日志记录。日志说:

Output:
Thr:9928 Waiting...
Thr:8532 Waiting...
Thr:8532 Started

这意味着在主线程将notify_all()发送到等待线程之后,其中只有一个实际醒来了。 如果我在ParseInstruction()内注释掉对Func()的调用,那么两个线程都会被唤醒,否则只有一个线程会这样做。 得到一些建议会很棒。

1 个答案:

答案 0 :(得分:6)

假设Func读取signaled并将其视为错误。

然后Parse设置signaled为真并执行notify_all;此时Func没有等待,所以看不到通知。

Func然后等待条件变量并阻塞。

您可以通过在condMutex的作业周围锁定signaled来避免这种情况。

这是正确使用条件变量的正常模式 - 您需要在同一个互斥锁中测试和修改要等待的条件。