目录
exit&join
pthread_exit
线程终止有以下几种方法:
- 线程函数执行return语句;
- 线程函数内部调用pthread_exit函数;
- 其他线程调用pthread_cancel函数。
#include <pthread.h>
/**
* 结束关闭调用该方法的线程,并返回一个内存指针用于存放结果
* void *retval: 要返回给其它线程的数据
*/
void pthread_exit(void *retval);
当某个线程调用pthread_exit方法后,该线程会被关闭(相当于return)。线程可以通过retval向其它线程传递信息,retval指向的区域不可以放在线程函数的栈内。其他线程(例如主线程)如果需要获得这个返回值,需要调用pthread_join方法。
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);
案例(使用pthread_join接受线程接收返回的结果)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
typedef struct Result {
char* p;
int len;
}Result;
void* red_thread(void* arg) {
Result* result = (Result*)malloc(sizeof(Result));
// 解析
char code = *((char*)arg);
// 读取缓冲区
char* ans = (char*)malloc(sizeof(char) * 100);
while (1)
{
fgets(ans, 100, stdin);
// 是我的
if (ans[0] == code) {
// 接受到了回复的消息了
free(ans);
printf("red线程结束\n");
char* redans = strdup("红色风暴走了\n");
result->p = redans;
result->len = strlen(redans);
// 结束线程
pthread_exit((void*)result);
}
// 不是我的
else {
printf("红色风暴在等你\n");
}
}
}
void* blue_thread(void* arg) {
Result* result = (Result*)malloc(sizeof(Result));
// 解析
char code = *((char*)arg);
// 读取缓冲区
char* ans = (char*)malloc(sizeof(char) * 100);
while (1)
{
fgets(ans, 100, stdin);
// 是我的
if (ans[0] == code) {
// 接受到了回复的消息了
free(ans);
printf("blue线程结束\n");
char* redans = strdup("蓝色妖姬走了\n");
result->p = redans;
result->len = strlen(redans);
// 结束线程
pthread_exit((void*)result);
}
// 不是我的
else {
printf("蓝色妖姬在等你\n");
}
}
}
int main(int argc, char const *argv[])
{
pthread_t pid_red,pid_blue;
char red = 'r';
char blue = 'b';
Result* red_result = NULL;
Result* blue_result = NULL;
// 创建线程
pthread_create(&pid_red, NULL, red_thread, &red);
pthread_create(&pid_blue, NULL, blue_thread, &blue);
// 获取线程结果
pthread_join(pid_red, (void**)&red_result);
printf("红色风暴:%s\n", red_result->p);
pthread_join(pid_blue, (void**)&blue_result);
printf("蓝色妖姬:%s\n", blue_result->p);
// 释放资源
free(red_result->p);
free(red_result);
free(blue_result->p);
free(blue_result);
return 0;
}
这里会出现资源竞争的情况,控制台输入一次只能被其中一个线程响应
pthread_detach
这个方式就是使用异步自动回收,主线程不需要等待子线程就会自动回收,跟join不同,这里没有同步而是异步。
#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);
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);
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* fun(void* arg) {
printf("线程启动\n");
printf("working\n");
sleep(1);
pthread_testcancel(); // 取消点函数
printf("线程结束\n");
return NULL;
}
int main(int argc, char const* argv[])
{
// 创建线程
pthread_t tid;
pthread_create(&tid, NULL, fun, NULL);
// 取消子线程
if (pthread_cancel(tid) != 0) {
perror("pthread_cancel");
}
// pthread_cancel只是发出停止的命令,不会返回结果的
void* res;
pthread_join(tid, &res);
if (res == PTHREAD_CANCELED) {
printf("线程被取消了\n");
}
else {
printf("线程没有取消 code: %ld\n", (long)res);
}
return 0;
}