我有一种情况,我想创建一个执行任务的工作线程(主要是用一个线程构建一个线程池)。多个线程可以向其发布任务,并且线程有一个循环来运行它们。
只要正确使用锁定就不应该太难,我想,所以我的实现如下:
typedef std::function<void()> MyTask;
class MyTaskPool {
public:
MyTaskPool() {
this->closed = false;
this->thread = std::thread(std::bind(&MyTaskPool::run, this));
}
~MyTaskPool() {
this->closed = true;
this->conditionVariable.notify_one();
this->thread.join();
}
void post(MyTask task) {
{
std::lock_guard<std::mutex>(this->mutex);
this->tasks.push(task);
}
this->conditionVariable.notify_one();
}
private:
bool closed;
std::mutex mutex;
std::condition_variable conditionVariable;
std::thread thread;
std::deque<MyTask> tasks;
void run() {
while (true) {
boost::optional<MyTask> task;
{
std::lock_guard<std::mutex>(this->mutex);
if (this->closed)
return;
if (this->tasks.size() > 0) {
task = this->tasks.front();
this->tasks.pop_front();
}
}
if (task.is_initialized()) {
task();
} else {
std::unique_lock<std::mutex> lock(this->mutex);
this->conditionVariable.wait(lock);
}
}
}
}
构建它,测试它,它的工作原理。而且它易于使用;我可以创建一个新的MyTaskPool,并使用简单的lambda表达式向其发布任务。大!除了...在长时间使用这个东西后,它突然中断:this-&gt; tasks.front()失败 - 错误告诉我迭代器不能被解除引用。我的任务deque是......空的?从deque中添加和删除的代码都由锁保护,因此不应该发生这种情况。
任何人都可以看到错误 - 我很确定某种竞争条件是什么?
实际代码稍微复杂一些,因为它会对每个任务进行一些处理,但这与此示例无关。
答案 0 :(得分:1)
错误实际上是微不足道的并且几乎不可察觉:用于保护任务检索的锁不存储在局部变量中。这导致它被立即破坏 - 并因此被释放。
对于那些寻求实现类似内容的人来说,这里有我的代码现在的工作方式,大卫和山姆的评论已融入其中:
typedef std::function<void(MySharedResource)> MyTask;
class MyTaskPool {
public:
MyTaskPool() {
this->closed = false;
this->thread = std::thread(std::bind(&MyTaskPool::run, this));
}
~MyTaskPool() {
{
std::lock_guard<std::mutex> lock(this->mutex);
this->closed = true;
}
this->conditionVariable.notify_one();
this->thread.join();
}
void post(MyTask task) {
{
std::lock_guard<std::mutex> lock(this->mutex);
this->tasks.push(task);
}
this->conditionVariable.notify_one();
}
private:
bool closed;
std::mutex mutex;
std::condition_variable conditionVariable;
std::thread thread;
std::deque<MyTask> tasks;
void run() {
while (true) {
boost::optional<MyTask> task;
{
std::unique_lock<std::mutex> lock(this->mutex);
if (this->closed)
return;
if (this->tasks.size() > 0) {
task = this->tasks.front();
this->tasks.pop_front();
} else {
this->conditionVariable.wait(lock);
}
}
if (task.is_initialized()) {
task();
}
}
}
}