LINUX.ORG.RU

Убить поток


0

0

Есть код:

void *thread (void *ptr) { ... }

main() { pthread_t tid; pthread_create(&tid, NULL, thread, NULL); ... pthread_kill (tid, SIGTERM); ... }

так вот, вместо того, чтобы прибить поток, вываливается весь процесс. если в отладчике - Program received SIGTERM signal, Terminated. Если из консоли - просто Terminated. что неправильно?

anonymous

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

1. в multithreaded приложении приходящие сигналы, если их особо не обрабатывать, могут прервать любую активную нить, точнее любую из нитей, которая находится в (обычно прерывание делается перед выходом из функции/syscall'a) функции библиотеки pthread, либо в системном вызове (перечень тех и других можно в man'e получить), а если ты не зарегистрировал обработчик сигнала (что ты не сделал), то отработает обработчик по умолчанию, как и произошло, - твой процес был завершен.

2. далее, ты посылаешь через pthread_kill() сигнал твоей нитке, а из-за того, что в ней обработчик не зарегистрирован, отрабатывает обработчик по умолчанию (все обработчики наследуются от родительской нитки, в данном случае она main), а это просто exit().

3. чтобы тебе завершить твою вновь созданную нить, назовем ее thread_child, из другой нитки, допустим thread_main, ты должен в thread-function нитки thread_id это в явном виде сделать:

pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0x00);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0x00);

лучше всего это делать сразу вначале thread-function нитки thread_child.

затем в той нитке, из которой ты хочешь завершить thread_child, тебе нужно изобразить примерно такую комбинацию:

/* проверяем, не завершился ли thread_child */
if (pthread_kill(thread_child, 0) != ESRCH) {
/* посылаем thread_child служебный сигнал, который ее завершает */
pthread_cancel(thread_child);
/* закрываем дескриптор thread_child */
pthread_join(thread_child, 0x00);
}
/* убираем за собой мусор */
thread_child = 0;

4. все. thread_child - мертв, а процесс твой работает как ни в чем не бывало.

proff
()

POSIX threads & signals

2 proff:
Уважаемый proff, браво!

Два вопроса:

1) Нельзя ли было обойтись без pthread_kill и pthread_join? Разве недостаточно будет просто (например)
// при старте потока
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, ...)
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, ...) // ну это, ясен пень от логики потока зависит и необязательно
...
// в "убивающем потоке"
pthread_cancel(...)


Большая просьба, если так делать нельзя, объяснить причину.

2) Где можно почитать ПОДРОБНО про применение POSIX threads? Естественно, кроме манов (я почти наизусть их знаю) и POSIX'a - на него денег фирма не выделяет. Все, что мне удалось найти из более-менее приличного, это http://www-sld.slac.stanford.edu/HELP/DECTHREADS/PTHREAD_ROUTINES, но там тоже достаточно бедно. Естественно, интересует документация более подробная, чем самоучители, в которых в одном потоке пишется "Hello," в другом " world!" и потом долго объясняется как же сделать так, чтобы они выводились в нужном порядке :))

bison
()

POSIX threads & signals

И еще, вдогонку: По крайней мере на FreeBSD в манах не описано поведение pthread_setcancel*() при нулевом указателе в качестве второго аргумента. Так что, несмотря на то, что это работает, по-моему лучше этим не пользоваться (если, конечно, это поведение не описано в POSIX'е, которого, как я уже говорил, у меня, к сожалению, нет)

bison
()

Насколько я помню pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, ...) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, ...) - это поведение по умолчанию, в итоге можно опустить.

Но в данном случае если приходит cancelation request нить завершится только в cancelation point т.е. если нить в цикле - это не сработает.

Вообще-то использование pthread_cancel желательно избегать, поскольку при этом ресурсы могут не освобождатся (типа сделанные malloc/new:)) поскольку нельзя гарантировать что до этой точки дошло выполнение, правда помогают cleanup handlers - но надо дополнительные усилия. Такчто желательно пересмотреть решение таким образом чтобы не использовать pthread_cancel.

Если же без этого не получается то в приеденом proff примере pthread_kill можно убрать - ето всеголиш для убежденности что такая нить есть:) - я бы это поменял на pthread_kill(thread_child, SIGCONT) - вдруг нить остановлена SIGSTOP:))

Некоторые материалы есть на www.opengroup.ogr - правда искать надо:)

Good Luck!

tvn
()

POSIX threads & signals

Угу, спасибо за ссылку!

Хотя насчет pthread_cancel я не согласен - как ты сам сказал, существуют вызовы pthread_cleanup_push() и pthread_cleanup_pop(), которые позволяют полностью контролировать процесс выделения/освобождения ресурсов. А на С++, с их помощью, это вообще делается вообще просто и красиво :)

bison
()

2bison: я посмотрел на ссылочку http://www-sld.slac.stanford.edu/HELP/DECTHREADS/PTHREAD_ROUTINES и хочу сказать вот что: это даже беднее, чем man на раздел pthread под Solaris. ;-)

теперь по твоим вопросам:

1. обойтись без pthread_kill() конечно же можно. но я бы этого не делал, т.к. профессиональному программеру положено быть параноиком;)

2. что касается pthread_join(), то тут вот что хочется сказать. под виндой ты знаешь, зачем нужна CloseHandle()? тут тоже самое. незачем, чтобы в экземпляре библиотеки pthread болталась структура (или много структур, если ниток много завершается), которая описывает твой уже отживший thread. а она будет болтаться, потому что люди, которые делали эту библиотеку тоже были отчасти параноиками, они не хотели иногда "терять" status'ы завершенных thread'ов.

3. что касается ссылок - лучшая ссылка это практика. причем на разных платформах;) но это справедливо, когда есть вопросы, на которые нет ответов;)

а если конструктивно, то лучшие источники - это коммерческие *nix платформы. а их много ;-)

http://www.sun.com/sunsoft/Products/Developer-products/sig/threads
http://developer.austin.ibm.com/sdp/library/ref/about4.1/df4threa.html
http://www.unix.digital.com/unix/smp
http://liinwww.ira.uka.de/bibliography/Os/threads.html
http://centaurus.cs.umass.edu/~wagner/threads_html/tutorial.html
http://developer.austin.ibm.com/sdp/library/aixpert/nov94/aixpert_nov94_intrm...
http://www.sun.com/sunworldonline/swol-02-1996/swol-02-threads.html
http://www.developer.ibm.com/sdp/library/aixpert/aug94/aixpert_aug94_PTHREADS...
http://developer.austin.ibm.com/sdp/library/aixpert/aug95/aixpert_aug95_threa...
http://developer.austin.ibm.com/sdp/library/aixpert/aug95/aixpert_aug95_signa...

думаю на этом пока остановимся;)

proff
()

вдогонку:
нулевой указатель в pthread_setcanceltype/state работает нормально на Solaris, HPUX, AIX и Tru64. только что ты сказал, что он и на freeBSD работает. что-ж, похвально для нулевого указателя;)
а если серьезно, то это для POSIX нормальная практика, передавать нулевой указатель, когда тебя не интересует старое значение изменяемого параметра.
p.s. конечно бывают грабли, но не в этом случае;)

proff
()

POSIX threads & signals

2 proff:

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

bison
()

2bison (man pthread_setcanceltype on Solaris):
pthread_setcanceltype() atomically sets the calling thread's
cancellation type to the specified type and, if oldtype is
not NULL, stores the previous cancellation type in oldtype.

думаю для лечения этого проявления паранойи, такой информации достаточно? ;-)

proff
()

POSIX threads & signals

Начал уж было писать, но подумал, что не хочется раздувать тред из такого пустяка. Скажу только, что маны по солярке != POSIX. Еще разок - на FreeBSD это в манах НЕ описано, хоть и работает. Поэтому, я считаю, что проще не полагаться на недокументированное поведение, тем более, что overhead от этого незначителен. А единственно верным решением является сверка с POSIXовой документацией.

bison
()

В стандарте описано именно так как сказано в манах по солярке:))

tvn
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.