带有deque的c ++任务线程中的竞争条件

时间:2016-12-12 13:21:11

标签: c++ multithreading c++11

我有一种情况,我想创建一个执行任务的工作线程(主要是用一个线程构建一个线程池)。多个线程可以向其发布任务,并且线程有一个循环来运行它们。

只要正确使用锁定就不应该太难,我想,所以我的实现如下:

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中添加和删除的代码都由锁保护,因此不应该发生这种情况。

任何人都可以看到错误 - 我很确定某种竞争条件是什么?

实际代码稍微复杂一些,因为它会对每个任务进行一些处理,但这与此示例无关。

1 个答案:

答案 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();
            }
        }
    }
}