Уничтожение (отмена) потока
Уничтожение (отмена) потока
Корректное завершение выполняющегося потока «извне», из другого потока (то есть асинхронно относительно прерываемого потока), — задача отнюдь не тривиальная; она намного сложнее аналогичной задачи прерывания процесса. Это связано с обсуждавшимся ранее при рассмотрении завершения потоков временем жизни объектов, которые могут быть использованы потоком к моменту его отмены (блоки динамической памяти, файловые дескрипторы, примитивы синхронизации и другие объекты системы).
Если для процесса в перечень «опасных» (с точки зрения завершения) объектов включаются только объекты со временем жизни выше уровня процесса (их число достаточно ограничено), то для потока в число таких объектов включаются уже все объекты со временем жизни процесса (process-persistent). Завершающийся (покидающий процесс) поток обязан оставить все объекты процесса в состоянии, пригодном для их дальнейшего использования другими потоками процесса.
Далее мы подробно рассмотрим то множество предосторожностей, которыми «обложена» отмена потока. Однако именно по причине их «множества» стоит сформулировать краткое правило: не пытайтесь завершать поток извне его функции потока, если для этого нет в высшей степени обоснованной необходимости (а такая необходимость действительно бывает, но крайне редко). Даже в крайнем случае следует рассмотреть возможность вместо отмены потока послать ему сигнал (даже не только «сигнал UNIX», а в более широком смысле — «некоторое сообщение»), который, обрабатываясь в контексте потока, после корректных завершающих действий вызовет его завершение. (Как обращаться с сигналами в потоке, будет детально рассмотрено позже.)
Для отмены (принудительного завершения) потока используется вызов:
int pthread_cancel(pthread_t thread);
где в качестве параметра thread указывается TID отменяемого потока. Однако этот вызов не отменяет поток, а только запрашивает завершение потока. В зависимости от статуса отмены, который мы сейчас рассмотрим, поток может перейти (или нет) к действию завершения, которое состоит в том, что:
• выполняются все процедуры завершения, занесенные ранее в стек завершения вызовами pthread_cleanup_push();
• выполняются деструкторы собственных данных потока;
• отменяемый поток завершается;
• процесс отмены — асинхронный с точки зрения вызывающего pthread_cancel() кода, поэтому вызывающий отмену поток должен дождаться завершения потока на вызове pthread_join().
Прежде всего, поток может вообще отказаться выполнять любые отмены, вызвав из своей функции потока:
int pthread_setcancelstate(int state, int* oldstate);
где state и oldstate — устанавливаемое и установленное ранее (возвращаемое вызовом) состояния отмены потока, которые могут принимать значения PTHREAD_CANCEL_DISABLE либо PTHREAD_CANCEL_ENABLE. (Естественно, как и во многих функциях с подобным прототипом, значением oldstate может быть NULL, и тогда нам не нужно возвращать ранее установленное состояние.)
Далее, даже если для потока установлено состояние завершаемости (также называемое «состоянием отмены») PTHREAD_CANCEL_ENABLE (это значение по умолчанию при создании потока), поток может переопределить еще и тип отмены, вызвав:
int pthread_setcanceltype(int type, int* oldtype);
где type и oldtype — как и в предыдущем случае, новое и ранее установленное значения типа отмены потока, которые могут принимать значения PTHREAD_CANCEL_ASYNCHRONOUS (асинхронный по отмене поток) либо PTHREAD_CANCEL_DEFERRED (синхронный по отмене поток). Значением по умолчанию, устанавливаемым при создании потока, является PTHREAD_CANCEL_DEFERRED, хотя предписываемым POSIX умолчанием является PTHREAD_CANCEL_ASYNCHRONOUS.
Обе рассмотренные функции установок[23] параметров отмены при успешном выполнении возвращают значение EOK.
Итак, действия потока на запрос его завершения будут определяться текущей комбинацией двух установленных для него параметров: состоянием и типом отмены.
Теперь о том, чем же отличается отмена асинхронно и синхронно завершаемых потоков. Поток с асинхронным типом отмены (установленный с PTHREAD_CANCEL_ASYNCHRONOUS) может быть отменен в любой произвольный момент времени, то есть он всегда «свободен» для отмены и отмена производится немедленно. Поток с синхронным типом отмены (установленный с PTHREAD_CANCEL_DEFERRED) может быть остановлен только в тех точках выполнения потока, когда ему «удобно», и соответствующие места в программе называются точками отмены. При поступлении запроса на отмену такого потока (после выполнения извне pthread_cancel()) запрос помещается в очередь, а процесс отмены активизируется только после того, как отменяемый поток в ходе своего выполнения достигнет очередной точки отмены. Как определяются (создаются) точки отмены в коде потока? Для этого служит функция:
void pthread_testcancel(void);
Каждый вызов pthread_testcancel() тестирует очередь поступивших запросов на отмену на предмет наличия запросов, и если таковой запрос есть, процесс отмены активизируется. Если в коде отсутствуют вызовы pthread_testcancel(), то в нем практически отсутствуют точки отмены и поток становится неотменяемым (подобно установке его состояния отмены в PTHREAD_CANCEL_DISABLE). Поэтому при выполнении длительных вычислений функцию pthread_testcancel() следует периодически вызывать в потоковой функции в тех точках, где потенциальная отмена потока не опасна.
Примечание
(Очень важно!) Достаточно много библиотечных функций могут сами устанавливать точки отмены. Более того, такие функции могут косвенно вызываться из других функций в программе и тем самым неявно устанавливать точки отмены. Информацию о таких функциях следует искать в справочной man-странице по функции pthread_testcancel(). В результате этого эффекта можно получить отмену потока не в той точке, которую вы считаете безопасной и которую явно отмечаете вызовом pthread_testcancel(), а ранее этой точки — когда будет вызвана одна из таких функций. А это, очевидно, вовсе не то, на что вы рассчитывали!
Если состояние отмены потока, как это описывалось ранее, установлено в PTHREAD_CANCEL_DISABLE, то никакая расстановка точек отмены не имеет эффекта и поток остается неотменяемым.
Покажем, как могут быть использованы все эти предосторожности в коде функции потока, чтобы сделать код безопасным с позиции возможной асинхронной отмены потока извне:
void* function(void* data) {
int state;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
// ... здесь выполняется инициализация ...
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
pthread_setcancelstate(&state, NULL);
while (true) {
struct blockdata *blk = new blockdata;
// ... обработка блока данных blk ...
delete blk;
pthread_testcancel();
}
}
...
pthread_t tid;
...
pthread_create(&tid, NULL, function, NULL);
...
pthread_cancel(tid); // отмена потока
void* res;
pthread_join(tid, &res); // ожидание отмены
if (res != PTHREAD_CANCELED)
cout << "Что-то не так!" << endl;
Наконец, в QNX (но не в POSIX) существует вызов, подобный pthread_cancel(), принудительно отменяющий поток независимо от его установок («желания»):
int pthread_abort(pthread_t thread);
В отличие от pthread_cancel(), этот вызов принудительно и немедленно отменяет поток. Кроме того, никакие процедуры завершения и деструкторы собственных данных потока не выполняются. Очевидно, что в результате такого «завершения» состояния объектов процесса будут просто неопределенными, поэтому такой вызов крайне опасен. При таком способе отмены в программный код, ожидающий завершения на pthread_join(), в качестве результата завершения возвращается константа (тип void*) PTHREAD_ABORTED (аналогично возвращается константа PTHREAD_CANCELED при выполнении pthread_cancel()).
Но и этих мер безопасности недостаточно на все случаи жизни, поэтому механизм потоков предусматривает еще один уровень (механизм) страховки.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКДанный текст является ознакомительным фрагментом.
Читайте также
10.4.5. Уничтожение процессом самого себя
10.4.5. Уничтожение процессом самого себя Процессы прерывают себя вызовом либо exit(), либо _exit(). Когда функция процесса main() возвращает управление, стандартная библиотека С вызывает exit() со значением, возвращаемым main() в качестве параметра.void exit(int exitCode);void _exit(int exitCode);Две формы,
10.4.6. Уничтожение других процессов
10.4.6. Уничтожение других процессов Разрушение другого процесса почти столь же просто, как создание нового — нужно просто уничтожить его:int kill(pid_t pid, int signum);pid должен быть идентификатором процесса, который требуется уничтожить, а signum описывает, как это нужно сделать.
Уничтожение программы
Уничтожение программы Любой объект на уровне MI, который можно создать, можно и уничтожить. Соответственно на каждую команду создания объектов MI приходится команда уничтожения. Пользователь на уровне MI устанавливает системный указатель на программу или другой объект MI и
Уничтожение полученного маршрута от отправителя
Уничтожение полученного маршрута от отправителя К сожалению, использование параметра маршрутизации образует брешь в системе обеспечения безопасности программ, выполняющих аутентификацию по IP-адресам (сейчас такая проверка считается недостаточной). Если хакер
Уничтожение cookies: забудь, кто я
Уничтожение cookies: забудь, кто я Как вам известно, вся информация о вас прописывается в cookies-файлах. Вы попадаете на файлообменный сервис, и сервер с радостью узнает (помимо IP), что вот он, юзер с логином User12345678 и паролем qwerty. Не дам ему качать, алчному хапуге, у меня в логах
Уничтожение
Уничтожение Допустим, вы обнаружили, что ваш компьютер кишмя кишит всевозможными вирусами. Что делать?– Ни в коем случае не поддавайтесь панике! Наверное, ни один вирус не сможет натворить столько бед, сколько смогут дрожащие руки перепуганного пользователя. Поэтому
3.1.3. Уничтожение процесса
3.1.3. Уничтожение процесса Для уничтожения процесса предназначена команда kill. Ей достаточно указать идентификатор требуемого процесса.Команда kill посылает процессу сигнал SIGTERM, являющийся запросом на завершение.[10] По умолчанию, если в программе отсутствует обработчик
4.2. Отмена потока
4.2. Отмена потока Обычно поток завершается при выходе из потоковой функции или вследствие вызова функции pthread_exit(). Но существует возможность запросить из одного потока уничтожение другого. Это называется отменой, или принудительным завершением, потока.Чтобы отменить
8.4.3. Динамическое создание и уничтожение массивов
8.4.3. Динамическое создание и уничтожение массивов Оператор new может выделить из хипа память для размещения массива. В этом случае после спецификатора типа в квадратных скобках указывается размер массива. Он может быть задан сколь угодно сложным выражением. new
14. Инициализация, присваивание и уничтожение класса
14. Инициализация, присваивание и уничтожение класса В этой главе мы детально изучим автоматическую инициализацию, присваивание и уничтожение объектов классов в программе. Для поддержки инициализации служит конструктор - определенная проектировщиком функция (возможно,
Уничтожение томатов
Уничтожение томатов К сожалению, в данный момент при столкновении сыра с помидорами ничего не происходит. Ситуацию надо исправить при помощи кода, добавленного в метод updatePosition, который приведен в листинге 11.30.Листинг 11.30// Уничтожаем помидоры при столкновении с сыромfor (int
Уничтожение одного элемента.
Уничтожение одного элемента. 1. Кликните дважды на элемент (заголовок, параграф, стихи и т. д.).2. Нажмите иконку BookCorrector "delete" или кликните правой кнопкой мышки внутри основного окна BookDesigner и затем нажмите "delete" в появившемся
3.3.3. Уничтожение фонового задания
3.3.3. Уничтожение фонового задания Сигнал о завершении посылается процессу командой kill:kill [-сигнал] номер_процессаДалее в этой книге мы рассмотрим, какие существуют сигналы. Пока же достаточно знать, что по умолчанию команда kill посылает сигнал номер 1 — HUP (hang?up -oтбой). На