Как должен быть реализован пул потоков в C?

я программирую на C++, но я использую только pthread.H, нет boost или C++11 потоков.

поэтому я пытаюсь использовать потоки, но на основе одного из моих предыдущих вопросов (ссылка), это кажется невозможным, так как потоки завершаются сразу после завершения своей задачи, и одна из наиболее распространенных причин использования реализации пула потоков-уменьшить накладные расходы на создание потоков, повторно используя эти потоки для нескольких задач.

Так это единственный способ реализовать это в C, чтобы использовать fork () и создать канал от основного к дочерним процессам? Или есть способ настроить трубу между потоками и их родителем, о котором я не знаю?

заранее большое спасибо!

6 ответов


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

это, как правило, немного проще/проще в C++, потому что немного легче договориться о некоторых интерфейсах (например, overload operator() для выполнения кода для задачи), но на фундаментальном уровне вы можете делать все то же самое в C (например, каждый task структура, которую вы помещаете в очередь, будет содержать указатель на функцию для выполнения работы для этой задачи).

в вашем случае, поскольку вы используете C++, вероятно, проще использовать перегрузку operator() чтобы сделать работу, хотя. Остальные task struct (или как вы его называете) будет содержать любые необходимые данные и т. д.


С POSIX standard:

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);
}

http://people.clarkson.edu/~jmatthew/cs644.archive/cs644.fa2001/proj/locksmith/code/ExampleTest/threadpool.c

Я использовал google пару месяцев назад, вы должны попробовать.

Edit: кажется, возможно, вам нужна группа. Я смог создать его с некоторыми незначительными изменениями выше, чтобы рабочий не выполнял работу, а просто соединял потоки.