线程处理—线程终止

线程处理—线程终止

1.相关函数

**线程终止有以下几种方法: **

  1. **线程函数执行 return 语句; **
  2. **线程函数内部调用 pthread_exit 函数; **
  3. 其他线程调用 pthread_cancel 函数。

1.1系统调用pthread_exit

线程终止函数为 pthread_exit:

1
2
3
4
5
6
#include <pthread.h>
/**
* 结束关闭调用该方法的线程,并返回一个内存指针用于存放结果
* void *retval: 要返回给其它线程的数据
*/
void pthread_exit(void *retval);

当某个线程调用 pthread_exit 方法后,该线程会被关闭(相当于 return)。线程 可以通过 retval 向其它线程传递信息,retval 指向的区域不可以放在线程函数的栈内。 其他线程(例如主线程)如果需要获得这个返回值,需要调用 pthread_join 方法。

1.2系统调用pthread_join

1
2
3
4
5
6
7
8
9
10
11
12
#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

1
2
3
4
5
6
7
8
9
10
11
12
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#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 测试例程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//
// 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测试例程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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 显示调用取消点函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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 异步取消

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#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禁用取消

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#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;
}