Книга: Основы программирования в Linux

Отмена потока

Отмена потока

Иногда требуется, чтобы один поток попросил другой завершиться досрочно способом, очень похожим на отправку ему сигнала. Сделать это можно с помощью потоков и параллельно с помощью обработки сигнала; у потоков появляется возможность изменить свое поведение, когда их просят завершиться.

Давайте сначала рассмотрим функцию для создания запроса на завершение потока.

#include <pthread.h>
int pthread_cancel(pthread_t thread);

Она достаточно проста: имея идентификатор потока, вы можете запросить его аннулирование. На приемном конце запроса на отмену все немного сложнее, но не слишком. Поток может установить состояние отмены с помощью функции pthread_setcancelstate.

#include <pthread.h>
int pthread_setcancelstate(int state, int *oldstate);

Первый параметр равен либо значению PHTREAD_CANCEL_ENABLE, позволяющему получать запросы на отмену, либо PTHREAD_CANCEL_DISABLE, заставляющему игнорировать подобные запросы. Указатель oldstate дает возможность получить предыдущее состояние. Если оно вас не интересует, можно просто передать в этом параметре NULL. Если запросы на отмену принимаются, есть второй уровень управления, принимаемый потоком, — тип отмены, который задается функцией pthread_setcanceltype.

#include <pthread.h>
int pthread_setcanceltype(int type, int *oldtype);

Тип отмены может принимать одно из следующих значений: PTHREAD_CANCEL_ASYNCHRONOUS, заставляющее обрабатывать запросы на отмену немедленно, и PTHREAD_CANCEL_DEFERRED, заставляющее запросы на отмену ждать, пока поток не выполнит одну из следующих функций: pthread_join, pthread_cond_wait, pthread_cond_timedwait, pthread_testcancel, sem_wait или sigwait.

Мы не описываем все эти функции в данной главе, поскольку, как правило, не все они нужны. Когда они понадобятся, вы сможете найти дополнительную информацию на страницах интерактивного справочного руководства.

Примечание

В соответствии со стандартом POSIX системные вызовы, способные задерживать выполнение, такие как read, wait и т.д., должны также быть точками отмены потока. Во время написания книги поддержка этого стандарта в ОС Linux представлялась незавершенной. Но кое-какая работа была проделана, скажем, некоторые задерживающие вызовы, такие как sleep, на самом деле допускают отмену. Для того чтобы обезопасить себя, добавляйте вызовы pthread_testcancel в программный код, который по вашим расчетам может быть отменен.

Параметр oldtype позволяет получить предыдущее состояние, если оно вас не интересует, можно передать NULL. По умолчанию потоки запускаются с состоянием отмены, равным PTHREAD_CANCEL_ENABLE, и типом отмены — PTHREAD_CANCEL_DEFERRED.

Выполните упражнение 12.7.

Упражнение 12.7. Отмена потока

Программа thread7.c — ещё один потомок программы thread1.с. На этот раз основной поток отправляет запрос на отмену потока, который он создал.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg);
int main() {
 int res;
 pthread_t a_thread;
 void *thread_result;
 res = pthread_create(&a_thread, NULL, thread_function, NULL);
 if (res != 0) {
  perror("Thread creation failed");
  exit(EXIT_FAILURE);
 }
 sleep(3);
 printf("Canceling thread...n");
 res = pthread_cancel(a_thread);
 if (res != 0) {
  perror("Thread cancelation failed");
  exit(EXIT_FAILURE);
 }
 printf("Waiting for thread to finish...n");
 res = pthread_join(a_thread, &thread_result);
 if (res != 0) {
  perror("Thread join failed");
  exit(EXIT_FAILURE);
 }
 exit(EXIT_SUCCESS);
}
void *thread_function(void *arg) {
 int i, res;
 res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
 if (res != 0) {
  perror("Thread pthread_setcancelstate failed");
  exit(EXIT_FAILURE);
 }
 res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
 if (res != 0) {
  perror{"Thread pthread_setcanceltype failed");
  exit(EXIT_FAILURE);
 }
 printf("thread_function is runningn");
 for(i = 0; i < 10; i++) {
  printf("Thread is still running (%d)...n", i);
  sleep(1);
 }
 pthread_exit(0);
}

Когда вы выполните эту программу, то увидите следующий вывод, демонстрирующий отмену потока:

$ ./thread7
thread_function is running
Thread is still running (0)...
Thread is still running (1)...
Thread is still running (2)...
Canceling thread...
Waiting for thread to finish...
$

Как это работает

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

sleep(3);
printf("Cancelling thread...n");
res = pthread_cancel(a_thread);
if (res != 0) {
 perror("Thread cancelation failed");
 exit(EXIT_FAILURE);
}

В созданном потоке вы сначала задаете состояние отмены, чтобы разрешить отмену потока:

res = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
if (res != 0) {
 perror("Thread pthread_setcancelstate failed");
 exit(EXIT_FAILURE);
}

Далее вы задаете тип отмены PTHREAD_CANCEL_DEFERRED:

res = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
if (res != 0) {
 perror("Thread pthread_setcanceltype failed");
 exit(EXIT_FAILURE);
}

И в конце поток ждет отмену:

for (i = 0; i < 10; i++) {
 printf("Thread is still running (%d)...n", i);
 sleep(1);
}

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


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