Я хочу знать, можно ли ждать в основном потоке без цикла while(1).
Запускаю несколько тредов через std::async()
и в каждом треде делаю подсчет чисел. После того, как я начну потоки, я хочу получить результаты обратно. Я делаю это с помощью std::future<>.get()
.
Когда я получаю результат, я вызываю std::future.get()
, который блокирует основной поток, пока не будет выполнен расчет в потоке. Это приводит к некоторому замедлению времени выполнения, если одному потоку требуется значительно больше времени, чем следующему, где вместо этого я мог бы выполнить некоторые вычисления с готовыми результатами, а затем, когда самый медленный поток будет выполнен, у меня, возможно, будет какой-то дополнительный расчет.
Есть ли способ приостановить основной поток до тех пор, пока ЛЮБОЙ из потоков не завершит работу? Я подумал о функции обратного вызова, которая пробуждает основной поток, но я до сих пор не знаю, как бездействовать основную функцию, не делая ее не отвечающей, например, на секунду, и вместо этого не запуская цикл while (true).
#include <iostream>
#include <future>
uint64_t calc_factorial(int start, int number);
int main()
{
uint64_t n = 1;
//The user entered number
uint64_t number = 0;
// get the user input
printf("Enter number (uint64_t): ");
scanf("%lu", &number);
std::future<uint64_t> results[4];
for (int i = 0; i < 4; i++)
{
// push to different cores
results[i] = std::async(std::launch::async, calc_factorial, i + 2, number);
}
for (int i = 0; i < 4; i++)
{
//retrieve result...I don't want to wait here if one threads needs more time than usual
n *= results[i].get();
}
// print n or the time needed
return 0;
}
uint64_t calc_factorial(int start, int number)
{
uint64_t n = 1;
for (int i = start; i <= number; i+=4) n *= i;
return n;
}
Я подготовил фрагмент кода, который работает нормально, я использую GMP Lib для больших результатов, но вместо этого код работает с uint64_t
, если вы вводите небольшие числа.
Если вы по какой-либо причине уже скомпилировали библиотеку GMP на своем ПК, вы можете заменить каждый uint64_t
на mpz_class
Похоже, вы ищете стандартное библиотечное решение для любой сигнальной модели WaitForMultipleObjects из winapi (или аналогичной модели из других бэкэндов). Этого не существует. Вы должны написать свой собственный код (предостережение: я не знаком с модулем параллелизма стандартной библиотеки, начиная с С++ 14, поэтому, если что-то было добавлено в С++ 20, ymmv).
Отвечает ли это на ваш вопрос? Ждете несколько фьючерсов?
@Куимби вроде как в самом вопросе. Это может быть подход с очередью сохранения потока и условной переменной. Ответ на вопрос слишком прожорлив к процессору
Какое именно поведение вы хотите, если один из результатов еще недоступен? Можете ли вы объяснить, что вы хотите, чтобы программа делала, если один поток занимает слишком много времени?
@JohnFilleau Я не хочу, чтобы программа останавливалась и ждала одного потока ... вычисления должны быть объединены вместе, и я хочу, чтобы, если одно объявление выполняется медленно, другие результаты объединялись параллельно.
Кстати, я изучил большинство предложенных вопросов, прежде чем отправить его, и вопрос @Quimby не появился.
Я связал это, потому что задача сбора - неплохая идея, ее можно реализовать с помощью условных переменных, чтобы быть более сонным.
Я бы подошел к этому несколько иначе.
Если у меня нет достаточно конкретной причины поступать иначе, я склонен подходить к большей части многопоточного кода одним и тем же общим способом: использовать (поточно-безопасную) очередь для передачи результатов. Поэтому создайте экземпляр потокобезопасной очереди и передайте ссылку на него каждому из потоков, которые выполняют генерацию данных. Любой поток, собирающий результаты, получает их из очереди.
Это делает автоматическим (и тривиальным) то, что вы создаете каждый результат по мере его создания, а не застреваете в ожидании результатов один за другим.
Я сделал цикл while и уже перебирал массив будущего, я не показывал это из соображений сложности, но, как я уже сказал, мне не нравится этот подход. Ваш ответ, кажется, удовлетворяет мои потребности: D
Из любопытства, как вы справляетесь с отказами в этом случае? Глобальный флаг? Специальное значение очереди?
@Quimby: немного зависит от типа обработки, но во многих случаях возвращается что-то вроде std::optional<Result>
.
Спасибо, это кажется самым чистым решением.
Вы можете использовать переменную условия, чтобы разбудить основной поток, как только будет готово каждое из фьючерсов. en.cppreference.com/w/cpp/thread/condition_variable