LINUX.ORG.RU

Thread pool на плюсах без join'ов

 ,


0

4

Добрый вечер, господа.

Есть следующая проблема: нужен thread pool (на современных плюсах написанный), от которого требуются всего две вещи:

1. Возможность асинхронно в него пихать задачи

2. После акта запихивания нужна возможность синхронно дождаться окончания выполнения всех задач. Немного погуглив, я нашел вот это: https://github.com/vit-vit/CTPL Но с этой либой есть две проблемы, маленькая и большая:

1. Меньшая в том, что насколько я понял, она не умеет из коробки принимать метод (а я хочу ту же семантику, что при передаче метода в конструктор std::thread). Но это я могу, наверное, исправить, подумав какое-то время, а пока просто метод в лямбду оборачиваю и пихаю ей.

2. А вот главная проблема в том, что эта либа реализует ожидание выполнения, вызывая на всех тредах .join(). И мне это очень не нравится, потому что итераций «запихнул - подождал» у меня планируется много, и они довольно короткие, и я не хочу каждый раз платить за создание новых тредов.

Поэтому, собственно, вопрос: как сделать так, чтобы когда переданный пулу метод вернет результат, рабочий тред не заканчивал работу, а засыпал на условной переменной, пока не появятся новые задачи? (Очевидный способ - чтобы он какой-нибудь atomic<bool> записывал в конце, но я хочу именно его засыпания на condition variable'е, а не polling loop в мастер-треде).

Если я дурак, а такая хрень по какой-либо причине не нужна, дайте знать, пожалуйста:)

Спасибо.

#include <future>
#include <memory>
#include <list>
#include <iostream>
std::list<std::future<void>> taskList;

void funct()
{
    std::cout << "1" << std::endl;
}

int main() 
{
  for (int i = 0; i<10; ++i) {
    auto task = std::async(std::launch::async, funct);
    taskList.push_back(std::move(task));
  }
  taskList.clear();
}
Dudraug ★★★★★ ()
Ответ на: комментарий от Dudraug

Да, я думал о future, но насколько пишут на SO, они только на винде, в вижуал студии за счет thread pool'а реализованы, а на линуксе каждый раз новый процесс создается (а именно этого я и пытаюсь избежать). Ну и кроме того, насколько я понимаю, futures для кучи однотипных задач не очень кошерно использовать, вроде бы, не для этого они, разве нет?

WizardOfOz ()

Не думал, что когда-нибудь скажу тут такое, но в данном случае свое бы вы написали быстре, чем если бы искали что-то готовое. Простейший пул на std::thread-ах и std::vector-а + какая-то примитивная реализация multi-producer/multi-consumer очереди для тасков. Пара часов работы.

Если, конечно, у вас нет еще каких-то хитрых и неозвученных требований.

eao197 ★★★★★ ()

У Anthony Williams есть глава «9.1.2 Waiting for tasks submitted to a thread pool». Похоже, что приведённый там код thread_pool'а подходит под твои требования.

utf8nowhere ★★ ()
Ответ на: комментарий от WizardOfOz

Ну про deferred... Выше верно заметили, что std::async имеет ряд серьезных проблем. Поэтому о deferred лучше вообще забыть, вообще по мне не нужная фича, которая наплодила флейма и проблем.

template<class... Args>
inline auto MyAsync(Args... args)
{
  return std::async(std::launch::async, args...);
}

Dudraug ★★★★★ ()
Ответ на: комментарий от WizardOfOz

Не, ну зависит от задачи. Если действительно нужно что-то умное и управляемое, то список future не вариант оф кос.

Dudraug ★★★★★ ()

Там вроде как почти всё есть.

...
if (++this->nWaiting == threads.size()) {
    waitingCV.notify();
}
...

void waitAll()
{
    std::unique_lock<std::mutex> lock(this->mutex);
    waitingCV.wait(lock, [this](){ return nWaiting == threads.size(); });
}

std::condition_variable waitingCV;
Не? Я ничего не проверял, только код просмотрел.

xaizek ★★★★★ ()

напиши пожалуйста что ты хочешь в принципе. просто пример кода как если бы у тебя был подходящий класс.

i36_zubov ()
Ответ на: комментарий от utf8nowhere

Да пофиг, как ты его используешь. Дотронулся до std::async — петух.

очень смешно...

asaw ★★★★★ ()

Спасибо большое, господа, вы мне очень помогли:)

WizardOfOz ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.