Это невозможно с Linux futex API, и я думаю, что это тоже можно доказать.
По сути, у нас есть сценарий, в котором N процессов должны быть надежно разбужены одним последним процессом, и, кроме того, ни один процесс не может касаться какой-либо общей памяти после окончательного пробуждения (поскольку она может быть уничтожена или повторно использована асинхронно). Хотя мы можем достаточно легко пробудить все процессы, фундаментальное состояние гонки находится между пробуждением и ожиданием; если мы вызовем пробуждение перед ожиданием, отставший никогда не проснется.
Обычное решение для чего-то подобного состоит в том, чтобы отставший проверял переменную состояния атомарно с ожиданием; это позволяет вообще не спать, если пробуждение уже произошло. Однако здесь мы этого сделать не можем — как только пробуждение становится возможным, трогать общую память небезопасно!
Еще один подход состоит в том, чтобы фактически проверить, все ли процессы уже перешли в спящий режим. Однако это невозможно с API фьютекса Linux; единственным показателем количества ожидающих является возвращаемое значение FUTEX_WAKE; если он возвращает меньшее количество официантов, чем вы ожидали, вы знаете, что некоторые из них еще не спали. Однако, даже если мы обнаружим, что разбудили недостаточное количество официантов, уже слишком поздно что-либо предпринимать — один из проснувшихся процессов, возможно, уже разрушил барьер!
Так что, к сожалению, этот тип примитива, который можно немедленно уничтожить, нельзя создать с помощью API фьютекса Linux.
Обратите внимание, что в конкретном случае с одним официантом, одним бодрствующим, может быть возможно обойти проблему; если FUTEX_WAKE возвращает ноль, мы знаем, что никто еще не проснулся, поэтому у вас есть шанс восстановиться. Однако превратить это в эффективный алгоритм довольно сложно.
Сложно добавить надежное расширение к модели фьютекса, которое исправило бы это. Основная проблема заключается в том, что нам нужно знать, когда N потоков успешно вошли в режим ожидания, и атомарно пробудить их все. Однако любой из этих потоков может выйти из ожидания для запуска обработчика сигнала в любое время — действительно, поток пробуждения также может выйти из ожидания для обработчиков сигналов.
Однако одним из возможных способов является расширение модели ключевого события в NT API. При событиях с ключом потоки освобождаются от блокировки парами; если у вас есть «релиз» без «ожидания», вызов «релиз» блокируется для «ожидания».
Этого самого по себе недостаточно из-за проблем с обработчиками сигналов; однако, если мы позволим вызову 'release' указать количество потоков, которые должны быть разбужены атомарно, это сработает. У вас просто есть каждый поток в барьере, уменьшающий счетчик, а затем «ждите» события с ключом по этому адресу. Последний поток "освобождает" N - 1 поток. Ядро не позволяет обрабатывать какие-либо события пробуждения до тех пор, пока все потоки N-1 не перейдут в это состояние события с ключом; если какой-либо поток покидает вызов фьютекса из-за сигналов (включая освобождающий поток), это вообще предотвращает любые пробуждения, пока все потоки не вернутся.
person
bdonlan
schedule
24.09.2011
pthread_barrier_destroy
синхронизируется только с последним ожидающим. в чем именно проблема? - person Hasturkun   schedule 04.08.2011pthread_barrier_destroy
, гарантируя, что никто не получит доступ к внутренним компонентам барьера, когда уничтожение будет успешным (чего не произойдет, если какой-либо поток ожидает). - person Hasturkun   schedule 25.09.2011pthread_barrier_destroy
. В последнем случае вы можете позже повторно инициализировать барьер для повторного использования. - person Hasturkun   schedule 25.09.2011PTHREAD_BARRIER_SERIAL_THREAD
) уничтожает, а затем разъединяет. - person Hasturkun   schedule 25.09.2011