Как должен быть реализован пул потоков в C?
я программирую на C++, но я использую только pthread.H, нет boost или C++11 потоков.
поэтому я пытаюсь использовать потоки, но на основе одного из моих предыдущих вопросов (ссылка), это кажется невозможным, так как потоки завершаются сразу после завершения своей задачи, и одна из наиболее распространенных причин использования реализации пула потоков-уменьшить накладные расходы на создание потоков, повторно используя эти потоки для нескольких задач.
Так это единственный способ реализовать это в C, чтобы использовать fork () и создать канал от основного к дочерним процессам? Или есть способ настроить трубу между потоками и их родителем, о котором я не знаю?
заранее большое спасибо!
6 ответов
Да, вы можете создать потокобезопасная очередь между нитями. Затем потоки в пуле будут сидеть в цикле, извлекая элемент из очереди, выполняя все, что ему нужно, а затем возвращаясь и получая другой.
это, как правило, немного проще/проще в C++, потому что немного легче договориться о некоторых интерфейсах (например, overload operator()
для выполнения кода для задачи), но на фундаментальном уровне вы можете делать все то же самое в C (например, каждый task
структура, которую вы помещаете в очередь, будет содержать указатель на функцию для выполнения работы для этой задачи).
в вашем случае, поскольку вы используете C++, вероятно, проще использовать перегрузку operator()
чтобы сделать работу, хотя. Остальные task
struct (или как вы его называете) будет содержать любые необходимые данные и т. д.
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);
(...) Поток создается при выполнении
start_routine
Сarg
как его единственный аргумент.
таким образом, вы должны создать кучу потоков с этой функцией, и все они выполняют функцию, которая идет что-то вроде
void *consumer(void *arg)
{
WorkQueue *queue = static_cast<WorkQueue *>(arg);
for (task in queue) {
if (task == STOP_WORKING)
break;
do work;
}
return WHATEVER;
}
(в конце ввода нажмите n STOP_WORKING
элементы в очередь, где n - число нити.)
имейте в виду, pthreads - это очень низкоуровневый API, который предлагает очень мало безопасности типов (все данные передаются как void
указатели). Если вы пытаетесь распараллелить CPU-интенсивные задачи, вы можете посмотреть на OpenMP.
'Не кажется возможным, так как потоки заканчиваются сразу после завершения своей задачи' что??
for(;;){
Task *myTask=theCommonProducerConsumerQueue->pop();
myTask->run();
}
.. никогда ничего не возвращайте, на самом деле, никогда не возвращайте.
вы можете найти его полезно посмотреть исходный код libdispatch, который является основой для Apple Grand Central Dispatch и использует пулы потоков.
Я бы предложил использовать Резьбовые Строительные Блоки от Intel для выполнения работы-очередь / threadpool, как задачи. Довольно надуманный пример использования TBB 3.0:
class PoorExampleTask : public tbb::task {
PoorExampleTask(int foo, tbb::concurrent_queue<float>& results)
: _bar(foo), _results(results)
{ }
tbb::task* execute() {
_results.push(pow(2.0, foo));
return NULL;
}
private:
int _bar;
tbb::concurrent_queue<float>& _results;
}
используется позже так:
tbb::concurrent_queue<float> powers;
for (int ww = 0; ww < LotsOfWork; ++ww) {
PoorExampleTask* tt
= new (tbb::task::allocate_root()) PoorExampleTask(ww, powers);
tbb::task::enqueue(*tt);
}
Я использовал google пару месяцев назад, вы должны попробовать.
Edit: кажется, возможно, вам нужна группа. Я смог создать его с некоторыми незначительными изменениями выше, чтобы рабочий не выполнял работу, а просто соединял потоки.