Я пытаюсь смоделировать систему, в которой есть несколько потоков, производящих данные, и один поток, потребляющий данные. Фокус в том, что я не хочу, чтобы выделенный поток потреблял данные, потому что все потоки живут в пуле. Вместо этого я хочу, чтобы один из продюсеров опорожнил очередь, когда есть работа, и уступите, если другой производитель уже освобождает очередь.Многопроцессорный однопользовательский Lazy Task Execution
Основная идея заключается в том, что есть очередь работы и блокировка обработки. Каждый производитель подталкивает свою полезную нагрузку в очередь, а затем предпринимает попытки ввести замок. Попытка неблокируется и возвращает либо true (блокировка была получена), либо false (блокировка удерживается кем-то другим).
Если блокировка получена, тогда этот поток обрабатывает все данные в очереди до тех пор, пока не станет пустым (включая любые новые полезные нагрузки, введенные другими производителями во время обработки). После того как вся работа была обработана, поток освобождает блокировку и завершает работу.
Ниже C++ код для алгоритма:
void Process(ITask *task) {
// queue is a thread safe implementation of a regular queue
queue.push(task);
// crit_sec is some handle to a critical section like object
// try_scoped_lock uses RAII to attempt to acquire the lock in the constructor
// if the lock was acquired, it will release the lock in the
// destructor
try_scoped_lock lock(crit_sec);
// See if this thread won the lottery. Prize is doing all of the dishes
if (!lock.Acquired())
return;
// This thread got the lock, so it needs to do the work
ITask *currTask;
while (queue.try_pop(currTask)) {
... execute task ...
}
}
В целом этот код работает отлично, и я фактически никогда не видел поведение я собираюсь описать ниже, но реализация заставляет меня чувствовать себя неловко. Разумеется, условие гонки возникает между тем, когда поток выходит из цикла while и когда он освобождает критический раздел.
Весь алгоритм основан на предположении, что если блокировка удерживается, то поток обслуживает очередь.
Я в основном ищу просветления на 2 вопроса:
- Я правильно, что есть состояние гонки, как описано (бонус для других рас)
- Есть стандартный шаблон для реализации этого механизма, является исполнителем и не предусматривает условия гонки?
Метод try_pop_or_unlock на самом деле является действительно неотразимой идеей. Как я могу сделать эту функцию атомной? – Mranz
Вы реализовали «очередь»? Простая поточно-безопасная «очередь» просто имеет в ней «mutex» - в этом случае просто разблокируйте переданную «блокировку», сохраняя при этом «mutex», когда «очередь» пуста. Для более удобной «очереди в очереди» все становится сложнее. – Yakk
К сожалению, эта реализация является последней. – Mranz