使用承诺数组的分段错误
Segmentation fault using array of promises
我编写了多线程程序,使用 promises 和 futures 来检查给定数字是否为素数。这个程序的正确性或效率不是重点,而是每次执行时发生的分段错误的事实——我在更复杂的程序中遇到过类似的问题,所以我写了这个简单的程序来理解它,但错误仍然存在这里。也许我不了解并发性,或者 promises/futures,但我认为这里的一切都以正确的方式完成......有人可以解释为什么它不起作用吗?这将非常有帮助:)
代码如下:
#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 < n; i++) futures[i] = promises[i].get_future();
- 如果 n>THREADS_NUMBER
然后它尝试 read/write from/to promises/futures 未分配。
问题出现在:
for (int i = 0; i < THREADS_NUMBER; i++)
threads[i] = std::thread{ [&]() { f(n, divisiors[i], promises[i]); }};
[&]
通过引用捕获所有变量。这包括 promises
和 i
。但是,lambda 中的代码尚未执行。它不会 运行 直到操作系统设置线程等。当线程开始执行时,主线程的 for
循环已经结束,变量 i
结束了它的生命周期。
但随后线程执行 promises[i]
,其中两者都是引用捕获。为此,它查找 promises
和 i
的引用。 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
.
我编写了多线程程序,使用 promises 和 futures 来检查给定数字是否为素数。这个程序的正确性或效率不是重点,而是每次执行时发生的分段错误的事实——我在更复杂的程序中遇到过类似的问题,所以我写了这个简单的程序来理解它,但错误仍然存在这里。也许我不了解并发性,或者 promises/futures,但我认为这里的一切都以正确的方式完成......有人可以解释为什么它不起作用吗?这将非常有帮助:)
代码如下:
#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 < n; i++) futures[i] = promises[i].get_future();
- 如果 n>THREADS_NUMBER
然后它尝试 read/write from/to promises/futures 未分配。
问题出现在:
for (int i = 0; i < THREADS_NUMBER; i++)
threads[i] = std::thread{ [&]() { f(n, divisiors[i], promises[i]); }};
[&]
通过引用捕获所有变量。这包括 promises
和 i
。但是,lambda 中的代码尚未执行。它不会 运行 直到操作系统设置线程等。当线程开始执行时,主线程的 for
循环已经结束,变量 i
结束了它的生命周期。
但随后线程执行 promises[i]
,其中两者都是引用捕获。为此,它查找 promises
和 i
的引用。 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
.