Книга: Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform

Тайм-ауты ядра и функция pthread_join()

Тайм-ауты ядра и функция pthread_join()

Самый простой пример для рассмотрения — это использование тайм-аута с функцией pthread_join(). Вот как это можно было бы сделать:

/*
 * tt1.c
*/
#include <stdio.h>
#include <pthread.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/neutrino.h>
#define SEC_NSEC 1000000000LL // В одной секунде
                              // 1 биллион наносекунд
void* long_thread(void *notused) {
 printf("Этот поток выполняется более 10 секундn");
 sleep(20);
}
int main(void) // Игнорировать аргументы
{
 uint64_t timeout;
 struct sigevent event;
 int rval;
 pthread_t thread_id;
 // Настроить событие — это достаточно сделать однажды
 // Либо так, либо event.sigev_notify = SIGEV_UNBLOCK:
 SIGEV_UNBLOCK_INIT(&event);
 // Создать поток
 pthread_create(&thread_id, NULL, long_thread, NULL);
 // Установить тайм-аут 10 секунд
 timeout = 10LL * SEC_NSEC;
 TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_JOIN, &event,
  &timeout, NULL);
 rval = pthread_join(thread_id, NULL);
 if (rval == ETIMEDOUT) {
  printf("Истекли 10 секунд, поток %d все еще"
   " выполняется!n",
   thread_id);
 }
 sleep(5);
 TimerTimeout(СLOCK_REALTIME, _NTO_TIMEOUT_JOIN, &event,
  &timeout, NULL);
 rval = pthread_join(thread_id, NULL);
 if (rval == ETIMEDOUT) {
  printf("Истекли 25 секунд, поток %d все еще выполняется"
   " (нехорошо)!n",
   thread_id);
 } else {
  printf("Поток %d завершен (как и ожидалось!)n",
   thread_id);
 }
}

Мы применили макроопределение SIGEV_UNBLOCK_INIT() для инициализации структуры события, но можно было установить sigev_notify в SIGEV_UNBLOCK и «вручную». Можно было даже сделать еще более изящно, передав NULL вместо struct sigevent — функция TimerTimeout() понимает это как знак, что нужно использовать SIGEV_UNBLOCK.

Если поток (заданный в thread_id) остается работающим более 10 секунд, то системный вызов завершится по тайм-ауту — функция pthread_join() возвратится с ошибкой, установив errno в ETIMEDOUT.

Вы можете использовать и другую «стенографию», указав NULL в качестве значения тайм-аута (параметр ntime в декларации выше), что предпишет ядру не блокироваться в данном состоянии. Этот прием можно использовать для организации программного опроса. (Хоть программный опрос и считается дурным тоном, его можно весьма эффективно использовать в случае с pthread_join(), периодически проверяя, завершился ли нужный поток. Если нет, можно пока сделать что-нибудь другое.)

Ниже представлен пример программы, в которой демонстрируется неблокирующий вызов pthread_join():

int pthread_join_nb(int tid, void **rval) {
 TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_JOIN,
  NULL, NULL, NULL);
 return (pthread_join(tid, rval));
}

Тайм-ауты ядра при обмене сообщениями

Все становятся несколько сложнее, когда вы используете тайм-ауты ядра при обмене сообщениями. Вспомните главу «Обмен сообщениями», раздел «Обмен сообщениями и модель «клиент/сервер») — на момент отправки клиентом сообщения сервер может как ожидать его, так и нет. Это означает, что клиент может заблокироваться как по передаче (если сервер еще не принял сообщение), так и по ответу (если сервер принял сообщение, но еще не ответил). Основной смысл здесь в том, что вы должны предусмотреть оба блокирующих состояния в параметре flags функции TimerTimeout(), потому что клиент может оказаться в любом из них.

Чтобы задать несколько состояний, сложите их операцией ИЛИ (OR):

TimerTimeout(... _NTO_TIMEOUT_SEND | _NTO_TIMEOUT_REPLY,
 ...);

Это вызовет тайм-аут всякий раз, когда ядро переведет клиента в состояние блокировки по передаче (SEND) или по ответу (REPLY). В тайм-ауте SEND-блокировки нет ничего особенного — сервер еще не принял сообщение, значит, ничего для этого клиента он не делает. Это значит, что если ядро генерирует тайм-аут для SEND-блокированного клиента, сервер об этом информировать не обязательно. Функция MsgSend() клиента возвратит признак ETIMEDOUT и обработка тайм-аута завершится.

Однако, как было упомянуто в главе «Обмен сообщениями» (параграф «_NTO_CHF_UNBLOCK»), если сервер уже принял сообщение клиента, и клиент желает разблокироваться, для сервера существует два варианта реакции. Если сервер не указал флаг _NTO_CHF_UNBLOCK на канале, по которому было принято сообщение, клиент будет разблокирован немедленно, и сервер не получит об этом никакого оповещения. У большинства серверов, которые мне доводилось встречать, флаг _NTO_CHF_UNBLOCK был всегда установлен. В этом случае ядро посылает серверу импульс, а клиент остается заблокированным до тех пор, пока сервер ему не ответит! Как было показано в вышеупомянутом разделе главы «Обмен сообщениями», это сделано для того, чтобы сервер мог узнать о запросе клиента на разблокирование и выполнить по этому поводу какие-то действия.

Оглавление книги

Оглавление статьи/книги

Генерация: 2.288. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз