Ответ 1
USe select()
с FIFO или сокетом, который вы можете вытолкнуть, чтобы разбудить его.
Я разрабатываю многопоточное приложение, которое использует темы POSIX. Я использую потоки для выполнения периодического задания и для этой цели я использую usleep (3), чтобы приостановить выполнение потока, Мой вопрос: как я могу отменить таймер usleep() из основного потока, я пробовал pthread_kill(thread, SIGALRM)
, но он имеет глобальный эффект, который приводит к прекращению основного приложения (по умолчанию). Вот мой псевдокод:
void threaded_task(void *ptr) {
initialize();
while(running) {
do_the_work();
usleep(some_interval);
}
clean_up();
release_resources();
}
И вот псевдофункция, которая используется для остановки (и изящное завершение), заданная нить из основного файла :
void stop_thread(pthread_t thread) {
set_running_state(thread, 0); // Actually I use mutex staff
// TODO: Cancel sleep timer so that I will not wait for nothing.
// Wait for task to finish possibly running work and clean up
pthread_join(thread, NULL);
}
Каков удобный способ достижения моей цели? Должен ли я использовать условные переменные или могу ли я сделать это с помощью вариантов sleep()?
USe select()
с FIFO или сокетом, который вы можете вытолкнуть, чтобы разбудить его.
Вы также можете спать с семафором (это на самом деле их реальная цель).
a sema_wait
в вашем потоке и sema_post
в вашем основном потоке. Это легко, чисто, портативно. Здесь ссылка на статью с подробным описанием процедуры:
http://www.netrino.com/node/202
Причина, по которой SIGALRM убивает все приложение, заключается в том, что вы, вероятно, не зарегистрировали для него обработчик сигналов. Действие по умолчанию для SIGALRM заключается в том, чтобы ядро завершило процесс, поэтому, если usleep
реализовано таким образом, который не использует SIGALRM (например, с помощью nanosleep
или одной из функций опроса с тайм-аутом), то usleep
не зарегистрировал бы обработчик или иначе не изменил бы расположение сигнала по умолчанию.
void handle_alrm(int sig) {
}
...
int main(void) {
signal(SIGALRM, handle_alrm);
...
должно быть достаточно, чтобы не убивать вашу программу, хотя вы должны заглянуть в более сложную функцию sigaction
, а не signal
, поскольку она позволяет больше контролировать и вести себя более последовательно на разных платформах.
Это может вызвать проблемы, если вы затем попытаетесь использовать код в системе, которая использует SIGALRM для реализации usleep
или sleep
, поэтому вы можете просто не использовать стандартные версии библиотек и использовать функции которые имеют более предсказуемую реализацию на всех платформах (возможно, тонкая оболочка вокруг nanosleep
, которая предоставляет необходимый интерфейс).
Мы используем wait на переменной условия с таймаутом, используя pthread_cond_timedwait
Когда мы хотим завершить работу, мы устанавливаем переменную 'shuting down' и делаем pthread_cond_broadcast
В качестве альтернативы select
также можно использовать переменные условия pthread (см. pthread_cond_init
, pthread_cond_wait
и pthread_cond_signal
), семафоры SysV или семафоры POSIX. Все они лучше подходят, чем usleep для потока обработки событий.
Похоже, вы работаете в Linux со ссылкой на man-страницу. Вы должны иметь возможность использовать нанослои и interupt с определением приложения (SIGRTMIN + x) для дочернего процесса. Nanosleep имеет функциональность, которая прерывается сигналами и возвращает оставшееся время, которое должно было спать. Вы также можете просто использовать сон, если вы используете большие периоды времени для сна.
Любой вид IPC, упомянутый выше, также может помочь вам решить эту проблему.
РЕДАКТИРОВАТЬ: Похоже, вы уже это делаете, за исключением того, что вы должны использовать сигнал, который не будет иметь внешних эффектов для программы. Любая из функций сна должна прерываться неблокированным сигналом. Сигналы в реальном времени предназначены для использования на основе каждого приложения.
Существует несколько способов сделать это:
eventfd(2)
для замены здесь)pthread_sigmask(3)
Может быть, вам нужно возиться с сигнальной маской или, может быть, сигналы не могут выйти из-под сна... Я не знаю. Я не знаю, что можно использовать sigwait() или sigtimedwait(). Мы используем pthread_kill, чтобы разбудить потоки, но мы спим с ними, используя sigwait.... не спящий. Это самый быстрый способ, который я нашел, чтобы разбудить это (40-50 раз быстрее, чем ждать на pthread_cond в соответствии с моими испытаниями.)
Мы делаем это перед созданием потоков:
int fSigSet;
sigemptyset(&fSigSet);
sigaddset(&fSigSet, SIGUSR1);
sigaddset(&fSigSet, SIGSEGV);
pthread_sigmask(SIG_BLOCK, &fSigSet, NULL);
Каждый созданный поток наследует эту маску. Я немного запутался в масках. Вы либо говорите системе, чтобы ничего не предпринимать для определенных сигналов, или, может быть, вы говорите системе, что работаете с некоторыми сигналами... Я не знаю. Кто-то еще может подключиться и помочь нам. Если бы я лучше поработал с масками, я мог бы сказать вам, что вы можете просто вставить этот код в свой ThreadProc. Кроме того, я не уверен, что SIGSEGV необходим.
Затем поток вызывает это для сна:
int fSigReceived;
// next line sleeps the thread
sigwait(&fSigSet, &fSigReceived); // assuming you saved fSigSet from above...
// you get here when the thread is woken up by the signal
// you can check fSigReceived if you care what signal you got.
Затем вы делаете это, чтобы разбудить поток:
thread_kill(pThread, SIGUSR1);