我不是那么多线程,所以我很感激任何建议
在我的服务器中,这是用生产者 - 消费者多线程风格写的
queue
与其mutex
和cv
:
template <typename Event>
struct EventsHandle {
public: // methods:
Event*
getEvent()
{
std::unique_lock<std::mutex> lock {mutex};
while (events.empty()) {
condition_variable.wait(lock);
}
return events.front();
};
void
setEvent(Event* event)
{
std::lock_guard<std::mutex> lock {mutex};
events.push(event);
condition_variable.notify_one();
};
void
pop()
{ events.pop(); };
private: // fields:
std::queue<Event*> events;
std::mutex mutex;
std::condition_variable condition_variable;
};
以及如何在消费者线程中使用它:
void
Server::listenEvents()
{
while (true) {
processEvent(events_handle.getEvent());
events_handle.pop();
}
};
并且在制片人中:
parse input, whatever else
...
setEvent(new Event {ERASE_CLIENT, getSocket(), nullptr});
...
void
Client::setEvent(Event* event)
{
if (event) {
events_handle->setEvent(event);
}
};
代码适用于linux,我不知道为什么,但在Windows MSVC13上失败
在某些时候,此对话框会抛出异常:
"Unhandled exception at 0x59432564 (msvcp120d.dll) in Server.exe: 0xC0000005: Access violation reading location 0xCDCDCDE1".
调试显示此行上出现异常:std::lock_guard<std::mutex> lock(mutex)
函数中的setEvent()
。
小小的谷歌搜索让我看到这些文章:
http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
http://www.codeproject.com/Articles/598695/Cplusplus-threads-locks-and-condition-variables
我试着跟着他们,但没什么帮助。所以在这篇长篇文章之后我的问题: 代码有什么问题,互斥?
更新
所以...最后在可爱的游戏名为“评论出线”之后,事实证明问题出在内存管理上。事件的执行者导致失败
作为结论,最好使用std :: unique_ptr&lt;&gt;提到Jarod42或在前后传递对象时使用值语义作为建议juanchopanza
尽可能使用库,不要重新发明轮=)
答案 0 :(得分:2)
由于EventsHandle::pop
中缺少互斥锁,您进行了数据竞争。
您的生产者线程可以通过调用setEvent()
将项目推送到队列,并在执行行events.push(event)
时被抢占。现在,消费者线程可以同时执行events.pop()
。最终在queue
上有两个未同步的写操作,这是未定义的行为。
另请注意,如果您有多个消费者,则需要确保您弹出的元素与之前从getEvent
检索到的元素相同。如果一个消费者在两个电话之间被另一个消费者抢占。使用两个独立的成员函数很难实现这一点,这些函数由作为类成员的互斥锁同步。这里通常的方法是提供单个getEventAndPop()
函数,在整个操作过程中保持锁定并摆脱当前的单独函数。这看起来似乎是一个荒谬的限制,但多线程代码必须遵循不同的规则。
答案 1 :(得分:1)
您可能还希望在互斥锁仍处于锁定状态时将setEvent
方法更改为不通知。它取决于调度程序,但等待通知的线程可能会立即唤醒,只是等待互斥锁。
void
setEvent(Event* event)
{
{
std::lock_guard<std::mutex> lock{mutex};
events.push(event);
}
condition_variable.notify_one();
};
答案 2 :(得分:0)
在我看来,pop应该集成在getEvent()中。
这将阻止更多线程获得相同的事件(如果pop不在getEvent中,那么更多的线程可以获得相同的事件)。
Event*
getEvent()
{
std::unique_lock<std::mutex> lock {mutex};
while (events.empty()) {
condition_variable.wait(lock);
}
Event* front = events.front();
events.pop();
return front;
};