线程处理—线程终止
1.相关函数
线程终止有以下几种方法:
- 线程函数执行 return 语句;
- 线程函数内部调用 pthread_exit 函数;
- 其他线程调用 pthread_cancel 函数。
1.1系统调用pthread_exit
线程终止函数为 pthread_exit:
#include <pthread.h>
/**
* 结束关闭调用该方法的线程,并返回一个内存指针用于存放结果
* void *retval: 要返回给其它线程的数据
*/
void pthread_exit(void *retval);
当某个线程调用 pthread_exit 方法后,该线程会被关闭(相当于 return)。线程 可以通过 retval 向其它线程传递信息,retval 指向的区域不可以放在线程函数的栈内。 其他线程(例如主线程)如果需要获得这个返回值,需要调用 pthread_join 方法。
1.2系统调用pthread_join
#include <pthread.h>
/**
* 等待指定线程结束,获取目标线程的返回值,并在目标线程结束后回收它的资源
*
* pthread_t thread: 指定线程 ID
* void **retval: 这是一个可选参数,用于接收线程结束后传递的返回值。如果非空,
pthread_join 会在成功时将线程的 exit status 复制到 *retval 所指向的内存位置。
如果线程没有显式地通过 pthread_exit 提供返回值,则该参数将被设为 NULL 或忽略
* return: int 成功 0
* 失败 1
*/
int pthread_join(pthread_t thread, void **retval);
1.3系统调用pthread_detach
#include <pthread.h>
/**
* @brief 将线程标记为 detached 状态。POSIX 线程终止后,如果没有调用
pthread_detach 或 pthread_join,其资源会继续占用内存,类似于僵尸进程的未回收
状态。默认情况下创建线程后,它处于可 join 状态,此时可以调用 pthread_join 等待
线程终止并回收资源。但是如果主线程不需要等待线程终止,可以将其标记为 detached
状态,这意味着线程终止后,其资源会自动被系统回收。
*
* @param thread 线程 ID
* @return int 成功返回 0,失败返回错误码
*/
int pthread_detach(pthread_t thread);
1.4系统调用pthread_cancel
#include <pthread.h>
/**
* @brief 向目标线程发送取消请求。目标线程是否和何时响应取决于它的取消状态和类
型
* 取消状态(Cancelability State):可以是 enabled(默认)或 disabled。如果
取消状态为禁用,则取消请求会被挂起,直至线程启用取消功能。如果取消状态为启用,
则线程的取消类型决定它何时取消。
* 取消类型(Cancelability Type):可以是 asynchronous(异步)或 deferred
(被推迟,默认值)。
* asynchronous:意味着线程可能在任何时候被取消(通常立即被取消,但系统
并不保证这一点)
* deferred:被推迟意味着取消请求会被挂起,直至被取消的线程执行取消点
(cancellation point)函数时才会真正执行线程的取消操作。
* 取消点函数:是在 POSIX 线程库中专门设计用于检查和处理取消请求的函数。
当被取消的线程执行这些函数时,如果线程的取消状态是 enabled 且类型是 deferred,
则它会立即响应取消请求并终止执行。man 7 pthreads 可以看到取消点函数列表。
*
* @param thread 目标线程,即被取消的线程
* @return int 成功返回 0,失败返回非零的错误码
* 需要注意的是,取消操作和 pthread_cancel 函数的调用是异步的,这个函数
的返回值只能告诉调用者取消请求是否成功发送。当线程被成功取消后,通过
pthread_join 和线程关联将会获得 PTHREAD_CANCELED 作为返回信息,这是判断取消是
否完成的唯一方式
*/
int pthread_cancel(pthread_t thread);
1.5系统调用pthread_setcancelstate
#include <pthread.h>
/**
* @brief 设置调用线程的取消状态
* PTHREAD_CANCEL_ENABLE:启用取消功能
* PTHREAD_CANCEL_DISABLE:禁用取消功能
*
* @param state 目标状态
* @param oldstate 指针,用于返回历史状态
* @return int 成功返回 0,失败返回非零错误码
*/
int pthread_setcancelstate(int state, int *oldstate);
6)pthread_setcanceltype
#include <pthread.h>
/**
* @brief 设置调用线程的取消类型
* PTHREAD_CANCEL_DEFERRED:设置取消类型为推迟
* PTHREAD_CANCEL_ASYNCHRONOUS:设置取消类型为异步
*
* @param type 目标类型
* @param oldtype 指针,用于接收历史类型
* @return int 成功返回 0,失败返回非零错误码
*/
int pthread_setcanceltype(int type, int *oldtype);
2.实例
2.1pthread_join 测试例程
//
// Created by root on 2024/9/17.
//
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
//定义结构体接收线程结果
typedef struct {
char *ptr;
int len;
} Result;
//线程tid_red的函数
void *thread_red(void *argv) {
//初始化Result结构体
Result *result1 = (Result *) malloc(sizeof(Result));
//获取线程参数
char *code = (char *) argv;
//创建缓冲区
char *buffer = (char *) malloc(sizeof(char) * 101);
while (1) {
fgets(buffer, 100, stdin);
if (buffer[0] == code[0]) {
free(buffer);
printf("Red thread exit\n");
char *res = strdup("Red她离开了\n");
result1->ptr = res;
result1->len = strlen(res);
//结束线程,返回结果
pthread_exit((void *) result1);
} else {
printf("Red thread continue\n");
}
}
}
//线程tid_white的函数
void *thread_white(void *argv) {
//初始化Result结构体
Result *result2 = (Result *) malloc(sizeof(Result));
//获取线程参数
char *code = (char *) argv;
//创建缓冲区
char *buffer = (char *) malloc(sizeof(char) * 101);
while (1) {
fgets(buffer, 100, stdin);
if (buffer[0] == code[0]) {
free(buffer);
printf("White thread exit\n");
char *res = strdup("White她离开了\n");
result2->ptr = res;
result2->len = strlen(res);
//结束线程,返回结果
pthread_exit((void *) result2);
} else {
printf("White thread continue\n");
}
}
}
int main(int argc, char *argv[]) {
//创建两个线程
pthread_t tid_red;
pthread_t tid_white;
char red_code = 'r';
char white_code = 'w';
Result *result_red=NULL;
Result *result_white=NULL;
//创建线程red
pthread_create(&tid_red, NULL, thread_red, &red_code);
//创建线程white
pthread_create(&tid_white, NULL, thread_white, &white_code);
//获取线程red的返回值
pthread_join(tid_red,(void **)&result_red);
printf("Red的结局%d是:%s\n",result_red->len,result_red->ptr);
//释放内存
free(result_red);
//获取线程white的返回值
pthread_join(tid_white,(void **)&result_white);
printf("White的结局%d是:%s\n",result_white->len,result_white->ptr);
//释放内存
free(result_white);
return 0;
}
2.2pthead_detach测试例程
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 回调函数
void *thread_tid(void *argv) {
// 使用 pthread_self() 获取当前线程的ID,并打印出来
printf("Thread tid:%lu, started\n", (unsigned long)pthread_self());
sleep(2); // 线程休眠2秒
printf("Thread finished\n");
return NULL; // 线程结束,返回NULL
}
int main(int argc, char *argv[]) {
// 创建线程
pthread_t tid;
int res_pthread_create = pthread_create(&tid, NULL, thread_tid, NULL);
if (res_pthread_create != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
// 分离线程
// 注意:一旦线程被分离,就不能再对其调用pthread_join了
int res_pthread_detach = pthread_detach(tid);
if (res_pthread_detach != 0) {
perror("pthread_detach");
exit(EXIT_FAILURE);
}
// 尽管线程已被分离,但这里仍然使用sleep来模拟等待
// 这并不是必须的,只是为了演示主线程和子线程的执行顺序
printf("主线程等待子线程结束(但实际上不需要等待,因为线程已被分离)\n");
sleep(3); // 主线程休眠3秒
printf("主线程结束\n");
// 注意:由于线程已被分离,其资源将在其终止时自动释放
// 因此,主线程不需要(也不能)调用pthread_join来等待该线程
return 0;
}
需要注意的是,pthread_detach 不会等待子线程结束,如果在后者执行完毕之前主 线程退出,则整个进程退出,子线程被强制终止。
2.3pthread_cancel测试例程
2.3.1 显示调用取消点函数
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 回调函数
void *thread_tid(void *argv) {
// 使用 pthread_self() 获取当前线程的ID,并打印出来
printf("Thread tid:%lu, started\n", (unsigned long)pthread_self());
//默认取消类型为延迟
printf("working\n");
sleep(1); // 线程休眠1秒
//显示调用取消点函数
pthread_testcancel();
printf("取消之后\n");
return NULL; //
}
int main(int argc, char *argv[]) {
// 创建线程
pthread_t tid;
int res_pthread_create = pthread_create(&tid, NULL, thread_tid, NULL);
if (res_pthread_create != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
//取消子线程
int res_pthread_cancel = pthread_cancel(tid);
if (res_pthread_cancel != 0) {
perror("pthread_cancel");
exit(EXIT_FAILURE);
}
void *res;
pthread_join(tid,&res);
if (res==PTHREAD_CANCELED){
printf("线程被取消\n");
}else{
printf("线程未被取消 exit code:%ld\n",(long)res);
}
return 0;
}
2.3.2 异步取消
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 回调函数
void *thread_tid(void *argv) {
// 使用 pthread_self() 获取当前线程的ID,并打印出来
printf("Thread tid:%lu, started\n", (unsigned long)pthread_self());
//默认取消类型为延迟
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
printf("working\n");
int i=0;
while(1){
printf("%d\n",i++);
}
}
int main(int argc, char *argv[]) {
// 创建线程
pthread_t tid;
int res_pthread_create = pthread_create(&tid, NULL, thread_tid, NULL);
if (res_pthread_create != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
//取消子线程
int res_pthread_cancel = pthread_cancel(tid);
if (res_pthread_cancel != 0) {
perror("pthread_cancel");
exit(EXIT_FAILURE);
}
void *res;
pthread_join(tid,&res);
if (res==PTHREAD_CANCELED){
printf("线程被取消\n");
}else{
printf("线程未被取消 exit code:%ld\n",(long)res);
}
return 0;
}
2.3.3禁用取消
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 回调函数
void *thread_tid(void *argv) {
// 使用 pthread_self() 获取当前线程的ID,并打印出来
printf("Thread tid:%lu, started\n", (unsigned long)pthread_self());
//禁用取消,直到调用pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)后才能取消
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
//默认取消类型为延迟
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
printf("working\n");
sleep(2);
printf("after sleep\n");
return NULL;
}
int main(int argc, char *argv[]) {
// 创建线程
pthread_t tid;
int res_pthread_create = pthread_create(&tid, NULL, thread_tid, NULL);
if (res_pthread_create != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
//取消子线程
int res_pthread_cancel = pthread_cancel(tid);
if (res_pthread_cancel != 0) {
perror("pthread_cancel");
exit(EXIT_FAILURE);
}
void *res;
pthread_join(tid,&res);
if (res==PTHREAD_CANCELED){
printf("线程被取消\n");
}else{
printf("线程未被取消 exit code:%ld\n",(long)res);
}
return 0;
}