使用 std::promise 和 std::future 时发生访问冲突

Access Violation occurs when using std::promise and std::future

在我的代码中,我使用大致如下所示的结构将任务分派到我的线程池:

working_data get_data(my_thread_pool & thread_pool) {
    size_t thread_pool_size = thread_pool.size();
    std::vector<working_data> data(thread_pool_size);
    std::vector<std::promise<void>> promises(thread_pool_size);
    
    std::mutex data_0_mutex;
    
    for(size_t i = 0; i < thread_pool_size; i++) {
        thread_pool.post_task([&, i] {
            std::unique_lock<std::mutex> lock(data_0_mutex, std::defer_lock);
            if(i == 0) 
                lock.lock();
            data[i].add_data(process_data());
            if(i != 0) {
                lock.lock();
                data[0].merge_from(data[i]);
            }
            promises[i].set_value();
        });
    }
    
    for(size_t i = 0; i < thread_pool_size; i++) {
        promises[i].get_future().wait();
    }
    
    return std::move(data[0]);
}

虽然这种情况不会发生每次我执行这段代码,但多次执行时,这段代码会导致访问冲突。

相反,以下代码永远不会导致访问冲突,但我宁愿不使用它,因为我不喜欢在末尾使用轮询循环:

working_data get_data(my_thread_pool & thread_pool) {
    size_t thread_pool_size = thread_pool.size();
    std::vector<working_data> data(thread_pool_size);
    std::vector<std::atomic_bool> flags(thread_pool_size);
    
    std::mutex data_0_mutex;
    
    for(size_t i = 0; i < thread_pool_size; i++) {
        thread_pool.post_task([&, i] {
            std::unique_lock<std::mutex> lock(data_0_mutex, std::defer_lock);
            if(i == 0) 
                lock.lock();
            data[i].add_data(process_data());
            if(i != 0) {
                lock.lock();
                data[0].merge_from(data[i]);
            }
            flags[i].store(true, std::relaxed_memory_order);
        });
    }
    
    for(size_t i = 0; i < thread_pool_size; i++) {
        while(!flags[i].load(std::relaxed_memory_order))
            std::this_thread::yield();
    }
    
    return std::move(data[0]);
}

请注意,主要区别在于我使用的是 std::atomic_bool 而不是 std::promisestd::future

错误很可能是在我的 [2000 多行] 代码中的其他地方引起的,但我想知道我在这里展示的代码中是否存在明显的错误。

编辑:

我观察到的另一件事:如果关闭优化,我将无法重现错误。此错误仅在打开优化时发生。

双重编辑组合:

发生访问冲突的调用堆栈不可读:

时间问题之一。

当最后一个线程设置它的 promise 值时,这允许主线程中的循环完成,这反过来导致 data_0_mutex 对象被销毁。最后一个线程仍然可以是 运行,当 lock 的析构函数被调用时,它引用的互斥锁已经(或正在)被销毁。

这在您的 "polling" 版本中不太可能是问题,因为主线程必须等待 OS 恢复它,但它仍然可能发生。

解决方案是在设置 promise 之前释放锁,方法是调用

lock.unlock();

promise[i].set_value();.

之前