«Это второй день моего участия в первом испытании обновлений 2022 года. Подробную информацию о мероприятии см.:Вызов первого обновления 2022 г."
1. Введение
В предыдущей статье было представлено создание, управление, использование и взаимодействие процессов в Linux, а также многопроцессорный параллелизм; в этой статье представлены основы использования потоков в Linux.
разница между потоком и процессом(1) Процесс: это наименьшая единица планирования операционной системы. В Linux вы можете просмотреть подробную информацию о процессе с помощью таких команд, как ps и top. (2) Поток: это наименьшая единица планирования процесса, и каждый процесс имеет основной поток. Главное, что нужно сделать в процессе, это нить.
(3) Во всей системе идентификатор процесса является уникальным идентификатором, и управление процессом осуществляется через PID. Каждый раз, когда создается процесс, ядро создает структуру для хранения всей информации о процессе, и каждый узел, в котором хранится информация о процессе, также сохраняет свой собственный PID. Этот идентификатор используется для управления процессом (например, для отправки сигнала). Когда дочерний процесс завершается и его необходимо перезапустить (дочерний процесс вызывает exit() для выхода или выполнения кода), это необходимо сделать с помощью системного вызова wait(). процесс-зомби, и его объект процесса больше не существует, но он будет занимать ресурсы PID, поэтому необходима переработка.
Для активного завершения потока необходимо вызвать pthread_exit(), а основной поток должен вызвать pthread_join() для перезапуска (при условии, что поток не устанавливает "атрибут отсоединения"). Отправка сигналов потока, таких как строки, также реализована через идентификаторы потоков.
Связь между процессами:A. Общая память B. Очередь сообщений C. Семафор D. Именованный канал E. Безымянный канал F. Сигнал G. Файл H.socketСвязь между потоками:A. Мьютекс B. Спин-блокировка C. Переменная условия D. Блокировка чтения-записи E. Сигнал потока F. Глобальная переменная
Связь между процессами требует либо переключения контекстов ядра, либо доступа к периферийным устройствам (именованным каналам, файлам). Так скорость будет ниже. Если поток принимает свой собственный уникальный метод связи, он в основном завершается в своем собственном пространстве процессов, и нет переключения, поэтому скорость связи будет выше. То есть методы связи, используемые между процессами и потоками, кроме разницы в типе, отличаются еще и скоростью.
Объяснение: Когда многопоточный процесс перехватывает сигнал, он блокирует только основной поток, а другие дочерние потоки продолжают выполняться, не затрагивая его.
2. Введение в функции, связанные с потоками
2.1 Создать тему
pthread_create — это функция для создания потоков в операционных системах Unix (Unix, Linux и т. д.).При компиляции необходимо указать библиотеку ссылок: -lpthread прототип функции
#include <pthread.h>
int pthread_create
(
pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg
);
Введение параметра
Первый параметр — это указатель на идентификатор потока. Второй параметр используется для установки свойств потока. NULL может быть заполнен по умолчанию. Третий параметр — это начальный адрес функции запуска потока. Последний параметр — это параметр для запуска функции. Вы можете заполнить NULL, если параметр не требуется. Посмотреть справку по функциям в Linux: # man pthread_create
возвращаемое значение:Возвращает 0, если поток был успешно создан. Если создание потока завершается неудачно, возвращается номер ошибки. После успешного создания потока параметр attr используется для указания различных атрибутов потока. Вновь созданный поток начинает выполняться с адреса функции start_rtn, которая имеет только один параметр универсального указателя arg.Если в функцию работы с потоком необходимо передать более одного параметра, необходимо поместить эти параметры в структуру, и затем поместите адрес этой структуры Pass в качестве аргумента arg.
Пример:
#include <stdio.h>
#include <pthread.h>
//线程函数1
void *pthread_func1(void *arg)
{
while(1)
{
printf("线程函数1正在运行.....\n");
sleep(2);
}
}
//线程函数2
void *pthread_func2(void *arg)
{
while(1)
{
printf("线程函数2正在运行.....\n");
sleep(2);
}
}
int main(int argc,char **argv)
{
pthread_t thread_id1;
pthread_t thread_id2;
/*1. 创建线程1*/
if(pthread_create(&thread_id1,NULL,pthread_func1,NULL))
{
printf("线程1创建失败!\n");
return -1;
}
/*2. 创建线程2*/
if(pthread_create(&thread_id2,NULL,pthread_func2,NULL))
{
printf("线程2创建失败!\n");
return -1;
}
/*3. 等待线程结束,释放线程的资源*/
pthread_join(thread_id1,NULL);
pthread_join(thread_id2,NULL);
return 0;
}
//gcc pthread_demo_code.c -lpthread
2.2 Выход из треда
Поток завершает выполнение, вызывая функцию pthread_exit, точно так же, как процесс вызывает exit, когда он завершается. Что делает эта функция, так это завершает поток, вызвавший ее, и возвращает указатель на объект.
Цель этой функции — завершить вызвавший ее поток и вернуть указатель на объект, который можно получить через второй параметр функции pthread_join.
прототип функции
#include <pthread.h>
void pthread_exit(void *retval);
разбор параметровАдрес, по которому должен вернуться поток. Примечание. Стек потока должен быть освобожден в конце потока, то есть функция потока должна вызвать pthread_exit() для завершения, иначе он не будет освобожден до тех пор, пока не завершится функция основного процесса.
2.3 Ожидание окончания потока
Функция pthread_join() ожидает блокирующего завершения потока, указанного параметром thread. Когда функция возвращается, ресурсы ожидающего потока возвращаются. Если поток завершился, функция немедленно возвращается. И поток, указанный потоком, должен быть присоединяемым атрибутом (совместный атрибут).прототип функции
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
параметрПервый параметр: идентификатор потока, то есть идентификатор потока, который идентифицирует уникальный поток. Последний параметр: определяемый пользователем указатель для хранения адреса, возвращаемого ожидающим потоком.возвращаемое значение0 означает успех. В случае неудачи возвращается номер ошибки. Пример получения возвращаемого значения потока:
//退出线程
pthread_exit ("线程已正常退出");
//接收线程的返回值
void *pth_join_ret1;
pthread_join( thread1, &pth_join_ret1);
2.4 Свойства разделения резьбы
Состояние создания потока по умолчанию — joinable (атрибут соединения).Если поток завершает выполнение, но не вызывает pthread_join, его состояние аналогично Zombie Process (процессу-зомби) в процессе, то есть еще остаются некоторые ресурсы, которые не были восстановлены (состояние выхода). код), поэтому создатель потока должен pthread_join дождаться завершения выполнения потока и получить код выхода потока и повторно использовать его ресурсы (аналогично ожиданию процесса, waitpid). Но после вызова функции pthread_join(pthread_id), если поток не завершит выполнение, вызывающая сторона будет заблокирована, что в некоторых случаях нежелательно.
Функция pthread_detach может установить состояние потока в отсоединенное (detached state), и все ресурсы будут автоматически освобождены после завершения выполнения потока.прототип функции
#include <pthread.h>
int pthread_detach(pthread_t thread);
параметридентификатор потокавозвращаемое значение0 означает успех. Ошибка возвращает код ошибки. Поток EINVAL не является присоединяемым потоком. ESRCH не имеет идентификатора потока для обнаружения.
2.5 Получить идентификатор текущего потока
Функция функции pthread_self заключается в получении идентификатора самого потока.прототип функции
#include <pthread.h>
pthread_t pthread_self(void);
возвращаемое значениеИдентификатор текущего потока. Тип pthread_t — unsigned long int, поэтому при печати используйте метод %lu, иначе будут проблемы с отображением результата.
2.6 Автоматически очищать ресурсы потока
Поток может запланировать вызов функций при выходе, такие функции называются обработчиками очистки потока. Он используется для последующей очистки ресурсов после аварийного завершения работы программы. В POSIX threading API предоставляетсяpthread_cleanup_push()/pthread_cleanup_pop()
Функции используются для автоматического освобождения ресурсов. отpthread_cleanup_push()
Точка вызоваpthread_cleanup_pop()
прекращение действий (включая вызовpthread_exit()
и аварийное завершение) будет выполнятьсяpthread_cleanup_push()
Указанная функция очистки.
Уведомление:pthread_cleanup_push
функция сpthread_cleanup_pop
Функции нужно вызывать парами.прототип функции
void pthread_cleanup_push(void (*routine)(void *),void *arg); //注册清理函数
void pthread_cleanup_pop(int execute); //释放清理函数
параметрvoid (*routine)(void *) : запись функции для обработчика. void *arg : формальный параметр, передаваемый функции-обработчику. int execute: значение состояния выполнения. 0 означает, что функция очистки не вызывается. 1 означает вызов функции очистки.
Условие, вызывающее вызов функции очистки:
- Вызов функции pthread_exit()
- Формальный параметр pthread_cleanup_pop равен 1. Примечание: возврат не вызывает вызова функции очистки.
2.7 Пример кода потока автоматической очистки
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
//线程清理函数
void routine_func(void *arg)
{
printf("线程资源清理成功\n");
}
//线程工作函数
void *start_routine(void *dev)
{
pthread_cleanup_push(routine_func,NULL);
//终止线程
// pthread_exit(NULL);
pthread_cleanup_pop(1); //1会导致清理函数被调用。0不会调用。
}
int main(int argc,char *argv[])
{
pthread_t thread_id; //存放线程的标识符
/*1. 创建线程*/
if(pthread_create(&thread_id,NULL,start_routine,NULL)!=0)
{
printf("线程创建失败!\n");
}
/*2.设置线程的分离属性*/
if(pthread_detach(thread_id)!=0)
{
printf("分离属性设置失败!\n");
}
while(1){}
return 0;
}
2.8 Функция отмены потока
Функция pthread_cancel — это функция отмены потока, используемая для отмены других потоков в том же процессе.
头文件: #include <pthread.h>
函数原型:pthread_cancel(pthread_t tid);