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

时间:2017-03-17 19:16:49

标签: c++ multithreading future access-violation

在我的代码中,我将任务分配给我的线程池,其构造或多或少看起来像这样:

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+行]代码中的其他地方引起的,但我想知道我在这里提出的代码中是否存在明显的错误。

编辑:

我观察到的另一件事:如果我关闭优化,我无法重新创建错误。只有在启用优化时才会出现此错误。

DOUBLE EDIT COMBO:

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

Call Stack

1 个答案:

答案 0 :(得分:1)

问题在于时机问题。

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

这在你的“轮询”版本中不太可能是一个问题,因为主线程必须等待操作系统恢复它,但它仍然可能发生。

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

lock.unlock();
promise[i].set_value();之前