Сигнализация всех потоков в процессе - PullRequest
9 голосов
/ 17 ноября 2010

Не сохраняя список текущих потоков, я пытаюсь увидеть, что сигнал в реальном времени доставляется всем потокам в моем процессе. Моя идея такова:

  • Первоначально обработчик сигнала установлен, и сигнал разблокирован во всех потоках.
  • Когда один поток хочет отправить сигнал «широковещания», он получает мьютекс и устанавливает глобальный флаг о том, что вещание происходит.
  • Отправитель блокирует сигнал (используя pthread_sigmask) для себя и входит в цикл, неоднократно вызывая raise(sig), пока sigpending не покажет, что сигнал находится в состоянии ожидания (не было потоков, остающихся с заблокированным сигналом).
  • Когда потоки получают сигнал, они воздействуют на него, но ожидают в обработчике сигнала очистки флага широковещания, чтобы сигнал оставался маскированным.
  • Отправитель завершает цикл, разблокируя сигнал (чтобы получить собственную доставку).
  • Когда отправитель обрабатывает собственный сигнал, он сбрасывает глобальный флаг, чтобы все остальные потоки могли продолжить свою работу.

Проблема, с которой я сталкиваюсь, заключается в том, что pthread_sigmask не соблюдается. Все работает правильно, если я запускаю тестовую программу под strace (предположительно, из-за разного времени планирования), но как только я запускаю ее в одиночку, отправитель получает свой собственный сигнал (несмотря на то, что заблокировал его ...?) И ни один из другие темы когда-либо назначаются.

Есть идеи, что может быть не так? Я попытался использовать sigqueue вместо raise, проверяя маску сигналов, добавляя sleep повсюду, чтобы убедиться, что потоки терпеливо ждут своих сигналов и т. Д., И теперь я в растерянности.

Редактировать: Благодаря ответу psmears, я думаю, я понимаю проблему. Вот потенциальное решение. Обратная связь была бы отличной:

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

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

Редактировать 2: Кажется, сейчас работает. Согласно документации для sigqueue, он возвращает EAGAIN, если не удается поставить в очередь сигнал. Но для надежности я решил просто продолжать вызывать sigqueue до тех пор, пока не будут запущены обработчики сигналов * 1054, чередуя вызовы на sched_yield после того, как я отправил num_threads-1 сигналов.

Во время создания потока было условие гонки, считая новые потоки, но я решил это странным (ab) использованием блокировок чтения-записи. Создание потока - «чтение», а широковещательный сигнал - «запись», поэтому, если поток не пытается передать, он не создает никакого конфликта при создании потока.

Ответы [ 4 ]

4 голосов
/ 17 ноября 2010

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

Вы, вероятно, можете обойти это, используя kill(getpid(), <signal>) для отправки сигнала текущему процессу в целом.

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

Можете ли вы описать более подробно, чего вы хотите достичь?И насколько портативным вы хотите, чтобы это было?Почти наверняка есть лучший способ сделать это (сигналы почти всегда являются головной болью, особенно когда они смешаны с потоками ...)

2 голосов
/ 17 ноября 2010

В многопоточной программе повышение (sig) эквивалентно pthread_kill (pthread_self (), sig). Попробуйте убить (getpid (), sig)

0 голосов
/ 09 февраля 2015

Вы пытаетесь синхронизировать набор потоков. С точки зрения шаблона проектирования, нативное решение вашей проблемы - это барьер.

0 голосов
/ 18 ноября 2010

Учитывая, что вы, очевидно, можете заблокировать создание и уничтожение потока, вы могли бы не просто заставить «широковещательный» поток публиковать необходимые обновления локального состояния потока в очереди для потока, который каждый поток проверяет всякий раз, когда он собирается использоватьпоток-локальное состояние?Если есть выдающиеся обновления, он сначала применяет их.

...