Здравствуйте, barney, Вы писали:
B>1) Какой механизм меж-потоковой синхронизации?
В общем случае код воркера выглядит где-то так:
queue_mutex.lock();
for(;;) {
if (shutdown_flag) {
break;
}
if (!queue.empty()) {
auto work = queue.pop_front(); // C++ - front, но потом pop_front, чтобы удалить
queue_mutex.unlock();
work();
queue_mutex.lock();
} else {
queue_cv.wait(&queue_mutex);
}
}
queue_mutex.unlock();
}
На входе в цикл воркера лочишь мьютекс и проверяешь очередь. Если она пуста, становишься в wait. wait() атомарно становится в очередь на сигнал и разлочивает мьютекс. Как именно ей указать мьютекс — зависит от API; в одних конкретный мьютекс это аргумент метода wait(), в других надо установить проперть у CV.
Дальше оно ждёт сигнала (одного или на всех), и выходит из wait(), залочивая тот же мьютекс.
Проверяешь снова, есть ли работа или приказ на shutdown.
Во время работы мьютекс надо явно отпустить, но после того, как работа вынута из очереди. Потом снова захватить.
Учти, что любой CV может получать stray interrupts, то есть из wait() вышли, но соответствующего signal()/notify() или signal_all()/broadcast() не было. Это норма, тогда надо без жалоб уйти на следующий цикл сна.
В общем всё. Можно обвесить счётчиками и т.п., но не принципиально.
Обычно очередь одна на всех, но можно делать, что диспетчер выбирает, к кому добавлять в очередь, это может быть удобнее в среде с дофига ядер и медленной синхронизацией кэша.
Укладка в очередь для такого подхода:
queue_mutex.lock();
queue.push_back(new_work);
queue_cv.signal();
queue_mutex.unlock();
B>Например я хочу гарантировать очередь исполнения моих ворков
B>допустим, из разных потоков я диспатчу:
B>dispatch(work1) | dispatch(work2)
B>я хочу чтобы work1 гарантированно исполнилась перед work2
B>при этом, чтобы потоки не простаивали
А вот тут никак, кроме как что диспетчер следит, что work2 требует завершения work1, и до этого завершения держит work2 в отдельной очереди, которую не кидает целевым воркерам.
Как именно — от темы thread pool никак не зависит, это тема структуры данных типа "граф задач".
Назовёшь это барьером или нет — несущественно.
B>2) Как эффективно передается Work между потоками?
B>Я так понимаю, что все thread используют общую память т.е. нет никакой нужды ничего никуда копировать? Или как?
Общая память. Но каждая передача между ядрами будет требовать сотню тактов. Поэтому часто их делать нельзя.
B>Будет ли эффективной передача std::function по значению?
Вполне.