LINUX.ORG.RU

Правильное завершение потока.

 ,


0

2

Есть примерно такая структура:

void *thread1(void *d) {
  while(1) {
    char *buf=malloc(0x100);
    fgets(stdin, buf, 0x100);
    // какаие-то действия
    free(buf);
  }  
}

int main() {
  pthread_t t;
  pthread_create(&t, 0, thread1, 0);
  // какаие-то действия

}

т.е. отдельный поток читает из stdin'а/трубы/чего-то ещё... как его правильно остановить (с очисткой памяти итп) по асинхронному событию из главного потока? например получению сигнала... или придётся как-то правильно читать, чтобы чтение было неблокирующим?

Ответ на: комментарий от Dennis7

А как хочешь. Куча же одна на всех.

то есть свой менеджер памяти придумывать? ох... костыли вы мои костыли...

и напоследок вопрос: для обмена небольшими сообщениями между потоками лучше свою очередь изобрести или пользоваться системными? насколько системная будет толще, чем обычная на семафорах?

Fat-Zer
() автор топика
Ответ на: комментарий от Fat-Zer

Использование pthread_cancel() подразумевает, что вы должны следить за памятью убиваемого потока. Следить можно по-разному, например:
- при помощи pthread_setcancelstate() в дочернем потоке управлять возможностью отмены процесса в разные моменты времени
- при помощи макросов pthread_cleanup_push() / pthread_cleanup_pop() установить локальный обработчик очистки при отмене потока (для Вас, наверное, самый быстрый способ)
- pthread_key_create() установить функцию-деструктор при отмене потока (подходит для долгоживущих данных)

Проверять утечки valgrind'ом.

Sorcerer ★★★★★
()
Ответ на: комментарий от Fat-Zer

Ну и кроме того, если объём временной выделяемой памяти невелик, можно воспользоваться alloca() - выделяет память на стеке, освобождать не требуется.

Sorcerer ★★★★★
()
Ответ на: комментарий от Sorcerer

The alloca() function returns a pointer to the beginning of the allo‐ cated space. If the allocation causes stack overflow, program behavior is undefined.

не учи человека плохому

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от DELIRIUM

С умом применять надо, и всё будет хорошо.

Sorcerer ★★★★★
()
Ответ на: комментарий от Sorcerer

Sorcerer, спасибо за инфу... лучше всего пойдёт pthread_cleanup_{push,pop}...

	do {
		char *buf= malloc(COMMAND_BUFF_SZ);
		pthread_cleanup_push(free, buf);

		rc = fgets(buf, COMMAND_BUFF_SZ, fs);
		if( rc !=0 ) {
			pthreed_cleanup_pop(1);
			break;
		}

		// ... буфер в другой поток передаётся

		pthreed_cleanup_pop(0);
	} while(1);

сделал так... пока не пробовал, будет ли работать...

Fat-Zer
() автор топика

а зачем в цикле внутри потока выделять память? можно выделить один раз, и вообще даже в главном потоке

Harald ★★★★★
()
Ответ на: комментарий от Sorcerer

ТС, конечно, не просил переносимого решения, но, тем не менее, стоит обратить его внимание, что _cancel'ом не рекомендуют пользоваться вплоть до полного отсутствия этого вызова в glib.

gag ★★★★★
()
Ответ на: комментарий от gag

Честно говоря, мне не совсем понятно, чем руководствуются разработчики ОС, не включая в свою систему функции posix 10-летней давности. :( У меня даже возникает вопрос о целесообразности поддержки таких ОС.

Sorcerer ★★★★★
()
Ответ на: комментарий от gag

а зачем в цикле внутри потока выделять память? можно выделить один раз, и вообще даже в главном потоке

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

В смысле через какую-нибудь переменную, которая проверяется в цикле. Зачем pthread_cancel?

внутрь системных вызовов проверку не впихнёшь...

ТС, конечно, не просил переносимого решения, но, тем не менее, стоит обратить его внимание, что _cancel'ом не рекомендуют пользоваться вплоть до полного отсутствия этого вызова в glib.

можно поподробнее... ман об этом молчит... и какой вариант переносимого способа?

Fat-Zer
() автор топика
Ответ на: комментарий от Fat-Zer

Не знаю насчёт общего решения. Но в частности надо посмотреть, какие вызовы в потоке будут блокировать.

У меня, скажем, был поток для чтения из сокета. Решение простое: создав pipe, получить два дескриптора. Тот, откуда читаем («служебный» канал), передать в поток. В потоке вместо блокирующего recv/read вызываем select с таймаутом 0 (т.е. бесконечность), предварительно подготовив для него payload-дескриптор & наш служебный дескриптор. Как только хочется поток убить, вместо убиения нежно пишем в служебный канал «end» или чего подобное. select в потоке мгновенно вернётся и после проверки, что сообщение поступило из служебного дескриптора, и оно «end», чистим всё и выходим из потока.

P.S. Сигналы - довольно общее решение, но они, если не ошибаюсь, прерывают выполнение во всех потоках.

gag ★★★★★
()
Ответ на: комментарий от Sorcerer

На кой такой геморрой? Чем не угодил стандартный вариант

thread1

while (running) {
 ...
}

thread2

running = false;
pthread_join(...);

Reset ★★★★★
()
Ответ на: комментарий от gag

да... пожалуй решение с select'ом самое приятное... + тогда всё чтение можно в один поток объединить... сейчас покорю man select_tut

по поводу сигналов: man говорит о существовании pthreed_kill(). по началу кстати думал придумать что-то, чтобы на сигналах системные вызовы возвращались с EINTR.

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