如何在两组线程之间来回正确转移控制

时间:2017-08-01 04:10:31

标签: c++ multithreading

我想用C ++ 11的标准库实现一个线程池。我想要公开的接口是允许我的主线程一次提交多个作业,并等待所有线程完成继续。这是我第一次明确处理线程,因此不可避免地遇到了一些死锁问题。这是我的代码:

class CrashQueue {
private:
    std::vector<std::thread> workers;
    std::queue<void*> payloads;
    std::function<void(void*)> function;
    std::mutex taskFetchingMutex;
    long aliveWorkers;

    std::condition_variable alarmClock;
    std::condition_variable sleepClock;
    std::mutex sleepClockMutex;
    bool running = true;

public:
    CrashQueue(std::size_t threadCount = std::thread::hardware_concurrency()) {
        for (std::size_t i = 0; i < threadCount; i ++) {
            workers.emplace_back([this]() -> void {
                while (running) {
                    void* payload;
                    {
                        std::unique_lock<std::mutex> lock(taskFetchingMutex);
                        if (payloads.empty()) {
                            aliveWorkers --;
                            if (aliveWorkers == 0)
                                sleepClock.notify_one();
                            alarmClock.wait(lock);
                            continue;
                        }
                        payload = payloads.front();
                        payloads.pop();
                    }

                    function(payload);
                }
            });
        }
    }

    ~CrashQueue() {
        running = false;
        alarmClock.notify_all();
        for (auto& worker : workers)
            worker.join();
    }

    void run() {
        this->aliveWorkers = workers.size();
        alarmClock.notify_all();

        std::unique_lock<std::mutex> lock(sleepClockMutex);
        sleepClock.wait(lock);
    }

    void commit(std::function<void(void*)>&& function, std::queue<void*>&& payloads) {
        this->function = std::move(function);
        this->payloads = std::move(payloads);
    }
};

我怀疑问题出现在构造函数的工作线程中执行的lambda表达式中:

if (payloads.empty()) {
    aliveWorkers --;
    if (aliveWorkers == 0)
        sleepClock.notify_one();
    alarmClock.wait(lock);
    continue;
}

可能是最后一个工作程序唤醒主线程并在主线程唤醒所有其他线程后睡眠的情况。尽管如此,它似乎不太可能,但每次我不处于调试模式时都会发生死锁。任何提示?

1 个答案:

答案 0 :(得分:0)

问题在某种程度上与我在这里使用两个互斥锁的事实有关。重写run如下所示:

void run() {
    this->aliveWorkers = workers.size();
    alarmClock.notify_all();

    while (true) {
        std::unique_lock<std::mutex> lock(taskFetchingMutex);
        if (aliveWorkers.load() == 0)
            break;
        sleepClock.wait(lock);
    }
}

但是,我无法将图片可视化以查看原始代码失败的原因。任何解释的答案仍然需要。

编辑:我看似正确的代码的完整来源:

#include <iostream>
#include <atomic>
#include <vector>
#include <thread>
#include <functional>
#include <condition_variable>
#include <mutex>
#include <random>
#include <algorithm>
#include <tuple>
#include <queue>

class CrashQueue {
private:
    std::vector<std::thread> workers;
    std::queue<void*> payloads;
    std::function<void(void*)> function;
    std::mutex taskFetchingMutex;
    std::atomic<long> aliveWorkers;

    std::condition_variable alarmClock;
    std::condition_variable sleepClock;

    bool running = true;

public:
    CrashQueue(std::size_t threadCount = std::thread::hardware_concurrency())
    : aliveWorkers(threadCount) {
        for (std::size_t i = 0; i < threadCount; i ++) {
            workers.emplace_back([this]() -> void {
                while (running) {
                    void* payload;
                    {
                        std::unique_lock<std::mutex> lock(taskFetchingMutex);
                        if (payloads.empty()) {
                            aliveWorkers.fetch_sub(1);
                            sleepClock.notify_one();

                            alarmClock.wait(lock);
                            continue;
                        }
                        payload = payloads.front();
                        payloads.pop();
                    }

                    function(payload);
                }
            });
        }

        // Make sure all workers finished running.
        while (aliveWorkers.load() > 0);
        std::unique_lock<std::mutex> lock(taskFetchingMutex);
    }

    ~CrashQueue() {
        running = false;
        alarmClock.notify_all();
        for (auto& worker : workers)
            worker.join();
    }

    void run() {
        this->aliveWorkers = workers.size();
        alarmClock.notify_all();

        while (true) {
            std::unique_lock<std::mutex> lock(taskFetchingMutex);
            if (aliveWorkers.load() == 0)
                break;
            sleepClock.wait(lock);
        }
    }

    void commit(std::function<void(void*)>&& function, std::queue<void*>&& payloads) {
        this->function = std::move(function);
        this->payloads = std::move(payloads);
    }
};