互斥体上的 C++ 并发段错误

C++ Concurrency segfault on mutex

你好,

我是 C++ 的新手,但我有 6 年 Java 经验,2 年 C 经验和一些并发基础知识。我正在尝试创建一个线程池来处理任务。它在下面与相关的测试主。

似乎错误是由

产生的
void ThreadPool::ThreadHandler::enqueueTask(void (*task)(void)) {
    std::lock_guard<std::mutex> lock(queueMutex);

正如我的调试器所说,但是在进行传统的 cout 调试时,我发现有时它可以在没有段错误和删除

的情况下工作
threads.emplace(handler->getSize(), handler);

来自 ThreadPool::enqueueTask() 大大提高了稳定性。

总的来说,我认为这也与我对condition_variable(称为闲人)的使用不当有关。

编译器:CLion 中的 minGW-w64

.cpp

#include <iostream>
#include "ThreadPool.h"

ThreadPool::ThreadHandler::ThreadHandler(ThreadPool *parent) : parent(parent) {
    thread = std::thread([&]{
        while (this->parent->alive){
            if (getSize()){
                std::lock_guard<std::mutex> lock(queueMutex);
                (*(queue.front()))();
                queue.pop_front();
            } else {
                std::unique_lock<std::mutex> lock(idlerMutex);
                idler.wait(lock);
            }
        }
    });
}

void ThreadPool::ThreadHandler::enqueueTask(void (*task)(void)) {
    std::lock_guard<std::mutex> lock(queueMutex);
    queue.push_back(task);
    idler.notify_all();
}

size_t ThreadPool::ThreadHandler::getSize() {
    std::lock_guard<std::mutex> lock(queueMutex);
    return queue.size();
}

void ThreadPool::enqueueTask(void (*task)(void)) {
    std::lock_guard<std::mutex> lock(threadsMutex);
    std::map<int, ThreadHandler*>::iterator iter = threads.begin();
    threads.erase(iter);
    ThreadHandler *handler = iter->second;
    handler->enqueueTask(task);
    threads.emplace(handler->getSize(), handler);
}

ThreadPool::ThreadPool(size_t size) {
    for (size_t i = 0; i < size; ++i) {
        std::lock_guard<std::mutex> lock(threadsMutex);
        ThreadHandler *handler = new ThreadHandler(this);
        threads.emplace(handler->getSize(), handler);
    }
}

ThreadPool::~ThreadPool() {
    std::lock_guard<std::mutex> lock(threadsMutex);
    auto it = threads.begin(), end = threads.end();
    for (; it != end; ++it) {
        delete it->second;
    }
}

.h

#ifndef WLIB_THREADPOOL_H
#define WLIB_THREADPOOL_H

#include <mutex>
#include <thread>
#include <list>
#include <map>
#include <condition_variable>

class ThreadPool {
private:
    class ThreadHandler {
        std::condition_variable idler;
        std::mutex idlerMutex;
        std::mutex queueMutex;
        std::thread thread;
        std::list<void (*)(void)> queue;
        ThreadPool *parent;
    public:
        ThreadHandler(ThreadPool *parent);
        void enqueueTask(void (*task)(void));
        size_t getSize();
    };
    std::multimap<int, ThreadHandler*> threads;
    std::mutex threadsMutex;
public:
    bool alive = true;
    ThreadPool(size_t size);
    ~ThreadPool();

    virtual void enqueueTask(void (*task)(void));
};


#endif //WLIB_THREADPOOL_H

主要内容:

#include <iostream>
#include <ThreadPool.h>

ThreadPool pool(3);

void fn() {
    std::cout << std::this_thread::get_id() << '\n';
    pool.enqueueTask(fn);
};

int main() {
    std::cout << "Hello, World!" << std::endl;
    pool.enqueueTask(fn);
    return 0;
}

您的 main() 函数调用 enqueueTask().

紧接着,您的 main() returns.

这让齿轮开始运转以结束您的过程。这涉及调用所有全局对象的析构函数。

ThreadPool 的析构函数然后继续删除所有动态作用域的线程。

线程还在运行。随之而来的是欢闹。

您需要实现所有线程有序关闭的过程。

意思是把active设置为false,把所有的线都踢到小腿上,然后加入所有的线,顺其自然,最后毁灭一切。

P.S。 -- 您需要修复 alive 的检查方式。您还需要使对 alive 的访问成为线程安全的,并受互斥体保护。问题是线程可能持有两个不同互斥量之一的锁。这使得这个过程有些复杂。此处需要进行一些重新设计。