Отмена потока с помощью pthread_cancel: хорошая практика или плохая
У меня есть программа на С++ в Linux (CentOS 5.3), создающая несколько потоков, которые находятся в бесконечном цикле для выполнения задания и сна в течение определенных минут.
Теперь мне нужно отменить текущие потоки, если появится новое уведомление о конфигурации и только что начал новый набор потоков, для которых я использовал pthread_cancel.
То, что я наблюдал, нити не прекращались даже после получения подтверждения отмены, даже некоторые спальные нити поднимались после того, как сон был завершен.
Поскольку поведение не было желательным, использование pthread_cancel в упомянутом сценарии ставит вопрос о хорошей или плохой практике.
Прокомментируйте использование pthread_cancel в вышеупомянутом сценарии.
Ответы
Ответ 1
В целом сокращение потока не очень хорошая идея. Лучше, когда это возможно, иметь общий флаг, который используется потоками для выхода из цикла. Таким образом, вы дадите потокам выполнить какую-либо очистку, которую им может понадобиться, прежде чем выходить.
В связи с тем, что потоки фактически не отменены, спецификация POSIX определяет набор точек отмены (man 7 pthreads). Темы могут быть отменены только в этих точках. Если ваш бесконечный цикл не содержит точку отмены, вы можете добавить ее, вызвав pthread_testcancel
. Если pthread_cancel
был вызван, то на этом этапе будет действовать.
Ответ 2
Если вы пишете безопасный код на С++ (см. http://www.boost.org/community/exception_safety.html), ваш код, естественно, готов к аннулированию потока. glibs выбрасывает исключение С++ при отмене потока, так что ваши деструкторы могут выполнить соответствующую очистку.
Ответ 3
Я бы использовал boost:: asio.
Что-то вроде:
struct Wait {
Wait() : timer_(io_service_), run_(true) {}
boost::asio::io_service io_service_;
mutable boost::asio::deadline_timer timer_;
bool run_;
};
void Wait::doWwork() {
while (run) {
boost::system::error_code ec;
timer_.wait(ec);
io_service_.run();
if (ec) {
if (ec == boost::asio::error::operation_aborted) {
// cleanup
} else {
// Something else, possibly nasty, happened
}
}
}
}
void Wait::halt() {
run_ = false;
timer_.cancel();
}
Как только у вас есть голова вокруг него, asio - прекрасный инструмент.
Ответ 4
Вы можете сделать эквивалент кода ниже.
#include <pthread.h>
#include <cxxabi.h>
#include <unistd.h>
...
void *Control(void* pparam)
{
try
{
// do your work here, maybe long loop
}
catch (abi::__forced_unwind&)
{ // handle pthread_cancel stack unwinding exception
throw;
}
catch (exception &ex)
{
throw ex;
}
}
int main()
{
pthread_t tid;
int rtn;
rtn = pthread_create( &tid, NULL, Control, NULL );
usleep(500);
// some other work here
rtn = pthtead_cancel( tid );
}