2016-12-02 9 views
2

Я написал многопоточную программу, используя обещания и фьючерсы, чтобы проверить, является ли заданное число простым. Правильность или эффективность этой программы не является точкой, а фактом ошибки сегментации, которая возникает при каждом выполнении - у меня была аналогичная проблема с более сложной программой, поэтому я написал этот простой, чтобы понять ее, и ошибка все еще Вот. Может быть, я не понимаю что-то о параллелизме или обещаниях/фьючерсах, но я думаю, что все сделано здесь надлежащим образом ... Может кто-нибудь объяснить, почему он не работает? Было бы очень полезно :)Ошибка сегментации с использованием массива обещаний

Вот код:

#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; 

} 

компилировать с g++ -std=c++11 -Wall -lpthread на Linux.

ответ

3

Проблема возникает с:

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

[&] фиксирует все переменные по ссылке. Это включает promises и i. Однако код внутри лямбда еще не выполнен. Он не запускается до тех пор, пока операционная система не установит поток и так далее. К моменту начала потока начинается цикл основного потока for, и переменная i закончила свою жизнь.

Но тогда нить выполняет promises[i], где оба они являются опорными захватами. Он ищет ссылки для promises и i для этого. promises - это хорошо, но i относится к переменной, которая больше не существует. Вероятно, segfault вызван i, имеющим значение мусора в результате этого поиска, а затем доступ за пределы.

Даже грузоподъемность int i; вне петли не устраняет проблему; то ссылка в lambda действительно найдет i, но она имеет окончательное значение после завершения цикла THREADS_NUMBER, что выходит за рамки для массива promises.

Чтобы устранить эту проблему, захват i значения:

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

Затем каждый лямбда использует значение i, как это было, когда была создана лямбда.


Примечание: Я думаю, что это нормально для std::thread иметь временный лямбда в качестве параметра конструктора, но не уверен, что либо. Стандарт, похоже, говорит, что конструктор потока копирует предоставленный функтор. Но все примеры cppreference, которые я искал, сначала создали имя lambda, а затем это имя, заданное как параметр конструктора, std::thread.

+0

Большое спасибо, я бы никогда не узнал его сам (на самом деле я трачу около 8 часов на эту проблему и никаких эффектов). Я никогда не использовал лямбда на C++, но они выглядели довольно прямолинейно для меня, поэтому я был Я думал, что это может быть проблема :) Спасибо снова! – sadida

+0

@sadida мне потребовалось много времени, чтобы выяснить, что происходит! –

1

Проблема возникает из-за этой строки: for (int i = 0; i < n; i++) futures[i] = promises[i].get_future(); - если n>THREADS_NUMBER, то он пытается читать/писать с/на обещания/фьючерсы, которые не были выделены.

+0

для меня он все еще дает segfault даже когда 'n == THREADS_NUMBER' –

+0

да, я тоже это заметил. @ M.M Я думаю, что ваш ответ ниже относительно 'i' захвата по ссылке - это решение основной проблемы! – Yatima

Смежные вопросы