c++ - 使用 promise 数组的段错误

标签 c++ multithreading c++11 future

我编写了多线程程序,使用 promises 和 futures 来检查给定数字是否为质数。这个程序的正确性或效率不是重点,而是每次执行时发生的段错误的事实——我在更复杂的程序中遇到过类似的问题,所以我写了这个简单的程序来理解它,但错误仍然存​​在这里。也许我不了解并发性或 promise / future ,但我认为这里的一切都以适当的方式完成......有人可以解释为什么它不起作用吗?这将非常有帮助:)

代码如下:

#include <future>
#include <thread>
#include <iostream>
#include <initializer_list>
#include <vector>
#include <cassert>


namespace {
const int THREADS_NUMBER = 8;

void f(int n, std::vector<int>& divisiors, std::promise<bool>& isPossiblePrime) {
    bool isPrimeCandidate = true;
    for (auto i : divisiors)
        if (n % i == 0) {
            isPrimeCandidate = false;
            break;
        }
    isPossiblePrime.set_value(isPrimeCandidate);
}


}

int main() {
    int n;
    std::cin >> n;
    assert(n > 2);
    std::promise<bool> promises[THREADS_NUMBER];
    std::future<bool> futures[THREADS_NUMBER];
    for (int i = 0; i < n; i++)
        futures[i] = promises[i].get_future();
    std::thread threads[THREADS_NUMBER];

    std::vector<int> divisiors[THREADS_NUMBER];
    for (int i = 2; i < n; i++)
        divisiors[i % THREADS_NUMBER].push_back(i);

    for (int i = 0; i < THREADS_NUMBER; i++) 
        threads[i] = std::thread{ [&]() { f(n, divisiors[i], promises[i]); }};

    bool isPrime = true;
    for(auto & f : futures) {
        bool out = f.get();
        isPrime = out && isPrime;
    }

    for (auto& t : threads)
        t.join();

    if(isPrime) std::cout << "PRIME" << std::endl;
    else std::cout << "NOT PRIME" << std::endl;

}

我在 Linux 上用 g++ -std=c++11 -Wall -lpthread 编译。

最佳答案

问题发生在:

for (int i = 0; i < THREADS_NUMBER; i++) 
    threads[i] = std::thread{ [&]() { f(n, divisiors[i], promises[i]); }};

[&] 通过引用捕获所有变量。这包括 promisesi。但是,lambda 中的代码尚未执行。直到操作系统设置好线程等,它才会运行。当线程开始执行时,主线程的 for 循环已经结束,变量 i 结束了它的生命周期。

但随后线程执行 promises[i],其中两者都是引用捕获。它会查找 promisesi 的引用以执行此操作。 promises 很好,但是 i 指的是一个不再存在的变量。段错误可能是由于 i 由于此查找而具有垃圾值,然后越界访问。

即使将 int i; 提升到循环之外也不能解决问题;然后 lambda 中的引用确实找到了 i,但它在完成 THREADS_NUMBER 循环后具有最终值,这超出了 promises数组。

要解决这个问题,按值捕获i:

threads[i] = std::thread{ [&,i]() { f(n, divisiors[i], promises[i]); }};

然后每个 lambda 使用创建 lambda 时 i 的值。


注意:我认为 std::thread 可以将临时 lambda 作为构造函数参数,但我对此也不确定。该标准似乎确实说线程构造函数复制了提供的仿函数。但是我搜索的所有 cppreference 示例都首先创建了一个命名的 lambda,然后将该名称作为构造函数参数提供给 std::thread

关于c++ - 使用 promise 数组的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40922845/

相关文章:

c++ - 当类型参数为空时无法实例化函数模板

c++ - 为什么不能将 auto 用作模板类型参数(例如 std::array<auto, 5>)?

c++ - 为什么 C++11 标准中的 INVOKE 工具引用数据成员?

c++ - 如何将字符数组部分添加到 vector<char>

java - Lucene 锁定异常

c# - 多线程处理集合中具有特定顺序 C# 的项目

c - 如何知道现有 Openmp 线程的数量

c++ - 如何在 SendInput 中使用扩展扫描码

c++ - Win32 C++ GUI - 获取输入文本并在另一个窗口中显示

c# - return 0xfe + ceil(x) 返回什么?