Вопрос

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

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

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

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

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

Является ли их способ либо:

  • Отправьте сигнал a boost::thread, или
  • Заставить istream "потерпеть неудачу", или
  • Убить Boost::thread?

Примечание:

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

Редактировать:

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

Спасибо!

Это было полезно?

Решение

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

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

Другие советы

Да, это так!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

В boost:thread вы ищете временное_соединение функция.

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top