我在这里有一个简单的例子:
自从我尝试学习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()
的调用,那么两个线程都会被唤醒,否则只有一个线程会这样做。
得到一些建议会很棒。
答案 0 :(得分:6)
假设Func
读取signaled
并将其视为错误。
然后Parse
设置signaled
为真并执行notify_all
;此时Func
没有等待,所以看不到通知。
Func
然后等待条件变量并阻塞。
您可以通过在condMutex
的作业周围锁定signaled
来避免这种情况。
这是正确使用条件变量的正常模式 - 您需要在同一个互斥锁中测试和修改要等待的条件。