pthread_kill () против pthread_cancel () для завершения потока, заблокированного для ввода-вывода - PullRequest
0 голосов
/ 18 сентября 2018

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

В соответствии с потоком у нас есть сценарий, в котором нам нужно завершить поток, заблокированный в poll (), из другого потока.Я сталкивался с функциями pthread_kill () и pthread_cancel (), которые могут завершить целевой поток, заблокированный для ввода-вывода.

При чтении справочных страниц обе эти функции работают нормально.Несколько ссылок в интернете позволяют предположить, что обе эти функции опасны для использования.

Есть ли альтернативный способ завершить поток, заблокированный для ввода-вывода?Если нет, то какую из этих функций рекомендуется использовать.

Ответы [ 3 ]

0 голосов
/ 18 сентября 2018

Нет такой вещи, как уничтожение потока.

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

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

Если вы согласны с тем, что поток, в конечном итоге, завершается в результате вызова, pthread_cancel - это правильный способ завершить поток, застрявший в блокировке.операция.Тем не менее, чтобы использовать его безопасно, вам нужно интенсивно использовать pthread_cleanup_push и pthread_cleanup_pop.

Если вы не хотите, чтобы поток завершался, сигналы - это единственный вариант.У вас есть два варианта:

  1. Установить обработчик сигнала (может быть недоступен), используя sigaction без SA_RESTART, так что это вызывает EINTR.Так как в этом подходе есть неотъемлемые условия гонки (если вы посылаете сигнал непосредственно перед тем, как вводится системный вызов блокировки, а не после того, как он заблокирован, сигнал ничего не будет делать), вам нужно повторно отправить сигналс экспоненциальным откатом, чтобы не истощать цель времени выполнения, пока цель не подтвердит с помощью какого-либо другого механизма синхронизации (хорошо работает семафор POSIX), что она получила сообщение.

  2. Установите обработчик сигнала, который будет longjmp.Чтобы сделать это безопасно, вам нужно контролировать контекст, из которого это может произойти;Самый простой способ сделать это - сохранить его в маске сигнала в обычном режиме, сняв маску только тогда, когда jmp_buf действителен для блокирующего вызова.Функция блокировки, которую вы вызываете, должна быть безопасна для асинхронных сигналов, и не должна быть той, которая выделяет или освобождает ресурсы (например, open или close), поскольку вы потеряете знанияо том, завершил ли он при обработке сигнала.Конечно, jmp_buf, или указатель на него, должен быть локальным для потока объектом (_Thread_local / __thread), чтобы это работало вообще.

0 голосов
/ 18 сентября 2018

Простой и понятный вариант - создать «сигнальную» трубу.То есть, вызовите pipe, возьмите дескриптор файла для конца «read» и добавьте его в список файловых дескрипторов pollPOLLIN).Затем, когда вы захотите разблокировать поток, ожидающий в poll, просто запишите байт в конец записи канала.Канал, получив данные, вернется как заблокированный в заблокированном потоке.Вы даже можете указать различные «команды», изменяя значение записанного байта.

(Вам необходимо прочитать байт из канала, прежде чем его можно будет повторно использовать, конечно.)

0 голосов
/ 18 сентября 2018

В зависимости от точной реализации вашей библиотеки потоков, весьма вероятно, что поток даже не вернется из poll при уничтожении, так что вы, вероятно, даже не достигнете того, чего хотите.

Вы должны быть очень осторожны, чтобы не создавать утечек памяти, и все же очень вероятно создать утечку файлового дескриптора, уничтожив принадлежащий ему поток (заметьте, ресурсы потока, в отличие от процессов, не"очищено" системой).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...