Убить заблокированный Boost :: Thread - PullRequest
20 голосов
/ 07 ноября 2008

Я пишу приложение, которое блокирует на входе два istreams.

Чтение с любого из istream является синхронным (блокирующим) вызовом, поэтому я решил создать два Boost::thread s для чтения.

Любой из этих потоков может добраться до «конца» (на основании полученного ввода), и как только «конец» достигнут, оба входных потока прекращают прием. К сожалению, я не могу знать, кто это сделает.

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

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

Есть ли у них способ:

  • Послать сигнал boost :: thread или
  • Принудительно заставить istream потерпеть неудачу или
  • Kill a Boost :: thread?

Примечание:

  • Один из istreams является cin
  • Я пытаюсь перезапустить процесс, поэтому я не могу закрыть входные потоки так, чтобы запретить их сброс.

Edit:

  • Я знаю, когда достигнут «конец», и я знаю, какой поток успешно завершился, а какой нужно убить. Это убийство, которое мне нужно выяснить (или другая стратегия чтения из istream).
  • Мне нужны оба потока для корректного выхода и очистки: (

Спасибо!

Ответы [ 9 ]

4 голосов
/ 06 декабря 2010

Да, есть!

boost::thread::terminate() выполнит работу в соответствии с вашими требованиями.

Это заставит целевой поток генерировать исключение. Предполагая, что он не обработан, стек будет правильно разматываться, уничтожая все ресурсы и прерывая выполнение потока.

Прекращение не мгновенно. (Во всяком случае, в этот момент работает не та нить.)

Это происходит в предопределенных условиях - наиболее удобным для вас, вероятно, будет вызов boost::this_thread::sleep();, который вы могли бы периодически выполнять этим потоком.

4 голосов
/ 08 ноября 2011

Если поток форсирования блокирует операцию ввода / вывода (например, cin>>whatever), boost::thread::terminate() не уничтожит поток. cin Ввод / вывод не является действительной точкой завершения. Улов 22.

4 голосов
/ 07 ноября 2008

Я не думаю, что есть способ сделать это кроссплатформенным, но pthread_cancel должен быть тем, что вы ищете. С потоком повышения вы можете получить native_handle из потока и вызвать в нем pthread_cancel.

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

1 голос
/ 21 августа 2012

У меня была похожая проблема, и я нашел это решение, которое некоторые другие читатели этого вопроса могут найти полезным:

Предполагая, что вы используете условную переменную с командой wait (), важно знать, что в Boost оператор wait () является естественной точкой прерывания. Так что просто поместите блок try / catch вокруг кода с помощью оператора wait и позвольте функции нормально завершиться в вашем блоке catch.

Теперь, предположив, что у вас есть контейнер с указателями потоков, выполните итерации по указателям потоков и вызовите interrupt () для каждого потока, а затем join ().

Теперь все ваши потоки будут корректно завершаться, и любая очистка памяти, связанная с Boost, должна работать без ошибок.

1 голос
/ 15 июля 2009

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

Дизайн boost.thread справляется с этим, управляя четко определенными точками прерывания. Я не знаю boost.asio хорошо, и кажется, что вы все равно не хотите на это полагаться. Если вы не хотите, чтобы рефакторинг использовал неблокирующую парадигму, вы можете использовать что-то между неблокирующим (опросом) и блокирующим IO. То есть сделать что-то вроде (псевдокод?):

while(!stopped && !interrupted)
{
    io.blockingCall(timeout);
    if(!stopped && !interrupted)
    {
        doSomething();
    }
}

Затем вы прерываете свои два потока и присоединяетесь к ним ...

Возможно, это проще в вашем случае? Если у вас есть главный поток, который знает, что один поток закончен, вам просто нужно закрыть IO другого потока?

Edit: Кстати, меня интересует окончательное решение, которое у вас есть ...

0 голосов
/ 16 февраля 2013

Очень поздно, но в Windows (и это предшественники, такие как VMS или RSX для тех, кто помнит такие вещи), я бы использовал что-то вроде ReadFileEx с подпрограммой завершения, которая сигнализирует о завершении, и CancelIO, если чтение необходимо отменить раньше. ,

Linux / BSD имеет совершенно другой базовый API, который не так гибок. Использование pthread_kill для отправки сигнала работает для меня, что останавливает операцию чтения / открытия.

Стоит реализовать разные коды в этой области для каждой платформы, ИМХО.

0 голосов
/ 08 августа 2009

Кажется, что потоки не помогают вам делать то, что вы хотите, простым способом. Если вам не нравится Boost.Asio, рассмотрите возможность использования select().

Идея состоит в том, чтобы получить два файловых дескриптора и использовать select(), чтобы сообщить вам, какой из них имеет входные данные. Дескриптор файла для cin обычно равен STDIN_FILENO; как получить другой, зависит от вашей специфики (если это файл, просто open() вместо использования ifstream).

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

0 голосов
/ 07 ноября 2008

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

ОДНАКО : Я только что обнаружил, что буст-мьютексы и т. Д. Не являются "оповещаемыми" на win32, поэтому QueueUserAPC не может их прервать.

0 голосов
/ 07 ноября 2008

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

В boost: тема, которую вы ищете для timed_join .

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

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

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