LINUX.ORG.RU

Многопоточное приложение

 , , ,


1

3

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

Вот, я перехватываю сигналы, например SIGINT. Кроме сигналов есть т.н. неисправимые ошибки в процессе выполнения.

При перехвате сигнала или появлении какой либо неисправимой ошибки я дергаю shutdown() завершение проги. Там все закрывается и освобождается.

Однако, естественно, что фатал эррор бывает вызывается из какого-либо потока, или даже, сигнал перехватывается «как-бы в контексте» потока. В таком случае вылезают понятные нативно, но не понятные в причинах касяки.

Собственно я сделал как. При старте проги получаю в (main контексте) thread_main = pthread_self() и устанавливаю threads_err = 0 Тогда в той части которая генерирует ошибку я проверяю в каком контексте она была вызвана (pthread_self() != thread_main), если это не в родительском main-потоке, то устанавливаю threads_err = 1 а цикл в main следит за состоянием этой переменной, и если ошибка есть, то вызывает shutdown()

Кажется мне, что это ГК. Или все же это нормальная практика? Нет? А как правильно?

Правильно делать так, как советуют отцы юникса — *все* сигналы обрабатывать в одном специальном потоке (конкретно — в главном). Для этого блокируешь все сигналы, нужные ждёшь в sigsuspend(), в хандлере сигналов парсишь номер сигнала, выставляешь нужные переменные, а в цикле, где sigsuspend(), проверяешь эти самые переменные и делаешь нужные телодвижения.

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

Ага, с сигналами ясно. А как быть с логическим фатал эррор в дочернем потоке? Например, я стартанул поток, он запустился, замалочил все что надо, и тут не смог открыть жизненно важный ему файл. В таком случае поток должен что-то куда-то срыгнуть, что в конечном счете повлечет за собой shutdown()

И, даже если вменяемо будет сгенерить какой-то сигнал для главного потока и его сигсуспенда - всеравно потребуется следить из какого именно потока был вызван этот спецсигнал. Ну и как я уже говорил - несколько раз я попадал на такое что сигнал был перехвачен «как-бы в контексте» дочернего потока. Почему я так думаю? Потому, что хендлер SIGINTа, который был проиничен в родительском потоке, у меня дергает генерацию фатал-эррор, и вот там я выдебажил что не всегда pthread_self() == thread_main.

Не, конечно, может я и что-то не то вдебажил, это я еще раз продебажу в любом случае.

deep-purple ★★★★★
() автор топика

вроде довольно типичное решение, пара моментов:
1 threads_err должна быть типа sig_atomic_t
2 нет смысла в проверке в каком потоке произошла ошибка

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

Т.е. ты не видишь смысла перевешивать все на сигсуспенд?

deep-purple ★★★★★
() автор топика

открой man 2 wait, там есть пример. В принципе ты верным путём идёшь.

emulek
()
Ответ на: комментарий от deep-purple

В таком случае поток должен что-то куда-то срыгнуть, что в конечном счете повлечет за собой shutdown()

Можешь послать сигнал в главный поток, можешь поднять какой-нибудь флаг, можешь даже объект исключения под локом добавить в общий вектор а в главном потоке вытащить его и даже rethrow'нуть - как тебе будет удобнее.

Ну и как я уже говорил - несколько раз я попадал на такое что сигнал был перехвачен «как-бы в контексте» дочернего потока.

Если ты заблочил сигналы в дочерних потоках, такого не может быть.

PS. Да, можешь посмотреть на promise/feature из c++11, они изкоробки умеют удобно передавать исключения из потоков.

slovazap ★★★★★
()
Последнее исправление: slovazap (всего исправлений: 1)
Ответ на: комментарий от deep-purple

А как быть

вот тебе пример из мана

       #include <sys/wait.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <stdio.h>

       int
       main(int argc, char *argv[])
       {
           pid_t cpid, w;
           int status;

           cpid = fork();
           if (cpid == -1) {
               perror("fork");
               exit(EXIT_FAILURE);
           }

           if (cpid == 0) {            /* Code executed by child */
               printf("Child PID is %ld\n", (long) getpid());
               if (argc == 1)
                   pause();                    /* Wait for signals */
               _exit(atoi(argv[1]));

           } else {                    /* Code executed by parent */
               do {
                   w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
                   if (w == -1) {
                       perror("waitpid");
                       exit(EXIT_FAILURE);
                   }

                   if (WIFEXITED(status)) {
                       printf("exited, status=%d\n", WEXITSTATUS(status));
                   } else if (WIFSIGNALED(status)) {
                       printf("killed by signal %d\n", WTERMSIG(status));
                   } else if (WIFSTOPPED(status)) {
                       printf("stopped by signal %d\n", WSTOPSIG(status));
                   } else if (WIFCONTINUED(status)) {
                       printf("continued\n");
                   }
               } while (!WIFEXITED(status) && !WIFSIGNALED(status));
               exit(EXIT_SUCCESS);
           }
       }

emulek
()
Ответ на: комментарий от deep-purple

правила игры в сигналы

1. Старайся не мешать нити и сигналы.

2. man 7 signal. Там есть список функций, которые можно безопасно вызвать из хендлера.

3. Файловые дескрипторы. write + select/poll для событий почти всегда лучше сигнала из нити в нить, инфа 100%ая.

AptGet ★★★
()
Ответ на: правила игры в сигналы от AptGet

3. Файловые дескрипторы. write + select/poll для событий почти всегда лучше сигнала из нити в нить, инфа 100%ая.

Вон выше elijah_sd говорит что sig_atomic_t мне должно хватить, ибо это просто глобальный флаг, работать дальше или уже склеивать ласты пора. Тем более, не важно, если хоть кто-то уже поставит его в true то все уже предрешено, отменять это состояние уже нет смысла.

deep-purple ★★★★★
() автор топика
Ответ на: комментарий от pon4ik

Можно, особенно если нитку можно завершить в любой момент. Если же нитка должна доделать какую-то работу и в ней уже есть event loop, точки отмены не очень: мультиплексирования с fd нет, придется дергать setcancelstate, для очистки придется назначить обработчик. Я думаю оно того не стоит, события на fd (ну или как там данные в нитку передаются) удобнее.

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

Точки отмены, если я ничего не забыл, влияют на любой сон. В том числе и на мьютексы, кондиционные переменные и слипы. Да и какая разница - флаг проверять или cancelstate ?

pon4ik ★★★★★
()

я дергаю shutdown() завершение проги

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

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

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

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