前言

创建线程:

  1. pthread_create()

退出线程:

  1. pthread_exit()return
  2. pthread_cancel()

线程的创建

使用多线程,首先就需要创建一个新线程。那么线程是如何被创建的呢,是用下面这个函数创建的。

  1. #include <pthread.h>
  2.  
  3. int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
  4. void *(*start_routine) (void *), void *arg);
  5.  
  6. //Compile and link with -pthread
  1.  

创建函数的四个参数的意义分别如下:

  1. thread :用来返回新创建的线程的 ID,这个 ID 就像身份证一样,指定了这个线程,可以用来在随后的线程交互中使用。
  2.  
  3. attr : 这个参数是一个 pthread_attr_t 结构体的指针,用来在线程创建的时候指定新线程的属性。如果在创建线程时,这个参数指定为 NULL, 那么就会使用默认属性。
  4.  
  5. start_routine :这个就是新线程的入口函数,当新线程创建完成后,就从这里开始执行。
  6.  
  7. arg arg 参数就是要传递给 start_routine 的参数。

返回值:如果函数执行成功,则返回 0,如果执行失败,则返回一个错误码。

错误码:

  1. EAGAIN :资源不足以用来创建一个新的线程,或者是达到了系统对线程数量的限制,请参考 setrlimit() /proc/sys/kernel/threads-max
  2. EINVAL :不可用的 attr
  3. EPERM :没有权限设置 attr 中的一下属性或者执行时序策略。

下面就是调用 pthread_create() 函数创建线程的一个例子:

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <errno.h>
  4.  
  5. void *
  6. thread_start(void *arg) {
  7. if(NULL == arg) {
  8. printf("[%u] : arg is NULL\n", (unsigned int)pthread_self());
  9. return NULL;
  10. }
  11. char * p = (char*)arg;
  12. printf("[%u] : arg = [%s]\n", (unsigned int)pthread_self(), p);
  13.  
  14. return NULL;
  15. }
  16.  
  17. int main() {
  18. pthread_t pt;
  19. int errn = pthread_create(
  20. &pt, //用来返回新创建的线程的 ID
  21. NULL, //使用默认的线程属性
  22. thread_start,//新线程从这个函数开始执行
  23. "hello"); //传递给新创建的线程的参数
  24.  
  25. if( != errn) {
  26. printf("error happend when create pthread, errno = [%d]\n", errn);
  27. if(EAGAIN == errn) {
  28. printf("Insufficient resources\n");
  29. } else if (EINVAL == errn) {
  30. printf("Invalid settings in attr\n");
  31. } else if (EPERM == errn) {
  32. printf("No permission\n");
  33. } else {
  34. printf("An error number that unexpected [%d], when create pthread\n", errn);
  35. }
  36.  
  37. return -;
  38. } else {
  39. printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
  40. }
  41. void *r = NULL;
  42. errn = pthread_join(pt, &r);
  43. if( != errn) {
  44. printf("error happend when join, errno = [%d]\n", errn);
  45. if(EDEADLK == errn) {
  46. printf("A deadlock was detected; or thread specifies the calling thread\n");
  47. } else if (EINVAL == errn) {
  48. printf("thread is not a joinable thread, or Another thread is already waiting to join with this thread\n");
  49. } else if (ESRCH == errn) {
  50. printf("No thread with the ID thread could be found\n");
  51. } else {
  52. printf("An error number that unexpected [%d], when join\n", errn);
  53. }
  54. return -;
  55. } else {
  56. printf("thread [%u] over\n", (unsigned int)pt);
  57. }
  58.  
  59. return ;
  60. }

接下来编译并运行,看看结果:

  1. gcc -g -c -o pthread_create.o pthread_create.c -Wall -I./
  2. gcc -g -o pthread_create pthread_create.o -Wall -I./ -lpthread
  3.  
  4. create thread success, threadid : []
  5. [] : arg = [hello]
  6. thread [] over

看起来执行成功了。下面再来看看一个线程的退出过程。

线程的退出

从上面的例子中,我们也可以看出,线程的入口,也就是一个函数,函数可以使用 return 进行退出, 那么在线程中,也是通过 return 进行退出的吗? 答案是,可以使用 return ,但是如果希望线程在退出的时候, 能够执行更多的动作,就不能使用 return 直接退出了,那么该怎样退出呢,可以使用 pthread_exit() 函数, 或者使用pthread_cancel() 函数。

这两个函数的原型如下:

  1. #include <pthread.h>
  2.  
  3. int pthread_cancel(pthread_t thread); //向指定的线程发送取消请求
  4. void pthread_exit(void *retval); //结束调用者线程
  5.  
  6. //Compile and link with -pthread

使用 pthread_exit() 退出线程

pthread_exit() 函数会结束当前进程。如果当前线程是可以被 join 的,则会通过参数 retval 返回一个值给同一个进程里面的另一个使用pthread_join(3) 函数的线程。

所有使用pthread_cleanup_push(3)函数压入栈的清理函数,都会被弹出并调用, 调用顺序是入栈时的反向顺序。如果线程有什么特别指定的数据,那么在所有的清理函数执行结束后, 会有适当的函数被调用,来析构这些数据,调用顺序不固定。

当一个线程终止后,进程内共享的资源(例如互斥信号量、条件变量、信号量以及文件描述符) 不会被释放。并且使用atexit(3)函数注册的函数也不会被调用。

当进程内的最后一个线程终止后,进程也就终止了,就像调用了exit(3)函数一样,并且参数是0. 这时候,进程内的共享资源就会被释放,并且使用 atexit(3) 函数注册的函数, 也会被调用。

下面来看一下 pthread_exit() 函数的一个例子:

  1. #include <stdio.h>
  2. #include <pthread.h>
  3.  
  4. void handlers(void *arg) {
  5. if(NULL != arg) {
  6. printf("%s() : [%s]\n", __func__, (char*)arg);
  7. } else {
  8. printf("%s()\n", __func__);
  9. }
  10. }
  11.  
  12. void *
  13. thread_start(void *arg) {
  14. printf("hello, this is thrad [%u]\n", (unsigned int)pthread_self());
  15. pthread_cleanup_push(handlers, "one");
  16. pthread_cleanup_push(handlers, "two");
  17. pthread_cleanup_push(handlers, "three");
  18.  
  19. //注意,这里执行了 pthread_exit() 函数
  20. pthread_exit("he~he~");
  21.  
  22. pthread_cleanup_pop();
  23. pthread_cleanup_pop();
  24. pthread_cleanup_pop();
  25.  
  26. return NULL;
  27. }
  28.  
  29. int main() {
  30. pthread_t pt;
  31. int errn = pthread_create(&pt, NULL, thread_start, NULL);
  32.  
  33. if( != errn) {
  34. printf("error [%d], when create pthread\n", errn);
  35. return -;
  36. } else {
  37. printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
  38. }
  39. void *r = NULL;
  40. errn = pthread_join(pt, &r);
  41. if( != errn) {
  42. printf("error happend when join, errno = [%d]\n", errn);
  43. return -;
  44. } else {
  45. printf("thread [%u] over\n", (unsigned int)pt);
  46. }
  47. if(NULL != r) {
  48. printf("thread return : [%s]\n", (const char*)r);
  49. }
  50.  
  51. return ;
  52. }

编译并运行:

  1. #include <stdio.h>
  2. #include <pthread.h>
  3.  
  4. void handlers(void *arg) {
  5. if(NULL != arg) {
  6. printf("%s() : [%s]\n", __func__, (char*)arg);
  7. } else {
  8. printf("%s()\n", __func__);
  9. }
  10. }
  11.  
  12. void *
  13. thread_start(void *arg) {
  14. printf("hello, this is thrad [%u]\n", (unsigned int)pthread_self());
  15. pthread_cleanup_push(handlers, "one");
  16. pthread_cleanup_push(handlers, "two");
  17. pthread_cleanup_push(handlers, "three");
  18.  
  19. //注意,这里执行了 pthread_exit() 函数
  20. pthread_exit("he~he~");
  21.  
  22. pthread_cleanup_pop();
  23. pthread_cleanup_pop();
  24. pthread_cleanup_pop();
  25.  
  26. return NULL;
  27. }
  28.  
  29. int main() {
  30. pthread_t pt;
  31. int errn = pthread_create(&pt, NULL, thread_start, NULL);
  32.  
  33. if( != errn) {
  34. printf("error [%d], when create pthread\n", errn);
  35. return -;
  36. } else {
  37. printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
  38. }
  39. void *r = NULL;
  40. errn = pthread_join(pt, &r);
  41. if( != errn) {
  42. printf("error happend when join, errno = [%d]\n", errn);
  43. return -;
  44. } else {
  45. printf("thread [%u] over\n", (unsigned int)pt);
  46. }
  47. if(NULL != r) {
  48. printf("thread return : [%s]\n", (const char*)r);
  49. }
  50.  
  51. return ;
  52. }

使用 return 退出线程

先来看一个使用 return 退出线程的例子:

  1. #include <stdio.h>
  2. #include <pthread.h>
  3.  
  4. void *
  5. thread_start(void *arg) {
  6. printf("hello, this is thread [%u]\n", (unsigned int)pthread_self());
  7.  
  8. return "ok";
  9. }
  10.  
  11. int main() {
  12. pthread_t pt;
  13. int errn = pthread_create(&pt, NULL, thread_start, NULL);
  14.  
  15. if( != errn) {
  16. printf("error [%d], when create pthread\n", errn);
  17. return -;
  18. } else {
  19. printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
  20. }
  21. void *r = NULL;
  22. errn = pthread_join(pt, &r);
  23. if( != errn) {
  24. printf("error happend when join, errno = [%d]\n", errn);
  25. return -;
  26. } else {
  27. printf("thread [%u] over\n", (unsigned int)pt);
  28. }
  29. if(NULL != r) {
  30. printf("thread return : [%s]\n", (const char*)r);
  31. }
  32.  
  33. return ;
  34. }

编译并运行:

  1. gcc -g -c -o pthread-return.o pthread-return.c -Wall -I./
  2. gcc -g -o pthread-return pthread-return.o -Wall -I./ -lpthread
  3.  
  4. ./pthread-return
  5. create thread success, threadid : [] //主线程打印的信息
  6. hello, this is thread [] //新创建的线程打印的信息
  7. thread [] over //主线程打印的信息
  8. thread return : [ok] //主线程打印的信息,其中[ok]为新创建的线程打印的信息

既然 return 和 pthread_exit() 函数都是结束线程,并返回数据,那么它们之间的区别是什么呢?

区别就在于,使用 return 退出线程的时候,不会执行线程使用 pthread_cleanup_push(3) 注册的清理函数。 可以再写一个例子,看看效果。

  1. #include <stdio.h>
  2. #include <pthread.h>
  3.  
  4. void handlers(void *arg) {
  5. if(NULL != arg) {
  6. printf("%s() : [%s]\n", __func__, (char*)arg);
  7. } else {
  8. printf("%s()\n", __func__);
  9. }
  10. }
  11.  
  12. void *
  13. thread_start(void *arg) {
  14. printf("hello, this is thrad [%u]\n", (unsigned int)pthread_self());
  15. pthread_cleanup_push(handlers, "one");
  16. pthread_cleanup_push(handlers, "two");
  17. pthread_cleanup_push(handlers, "three");
  18.  
  19. //注意,这里执行了 return
  20. return "he~he~";
  21.  
  22. pthread_cleanup_pop();
  23. pthread_cleanup_pop();
  24. pthread_cleanup_pop();
  25.  
  26. return "ok";
  27. }
  28.  
  29. int main() {
  30. pthread_t pt;
  31. int errn = pthread_create(&pt, NULL, thread_start, NULL);
  32.  
  33. if( != errn) {
  34. printf("error [%d], when create pthread\n", errn);
  35. return -;
  36. } else {
  37. printf("create thread success, threadid : [%u]\n", (unsigned int)pt);
  38. }
  39. void *r = NULL;
  40. errn = pthread_join(pt, &r);
  41. if( != errn) {
  42. printf("error happend when join, errno = [%d]\n", errn);
  43. return -;
  44. } else {
  45. printf("thread [%u] over\n", (unsigned int)pt);
  46. }
  47. if(NULL != r) {
  48. printf("thread return : [%s]\n", (const char*)r);
  49. }
  50.  
  51. return ;
  52. }

编译并运行:

  1. gcc -g -c -o pthread-return.o pthread-return.c -Wall -I./
  2. gcc -g -o pthread-return pthread-return.o -Wall -I./ -lpthread
  3.  
  4. ./pthread-return
  5. create thread success, threadid : []
  6. hello, this is thrad []
  7. thread [] over
  8. thread return : [he~he~]

可以看出,确实没有执行清理函数,为什么呢?

因为pthread_cleanup_push(3) 和 pthread_cleanup_pop() 是使用宏实现的。 在 pthread_cleanup_push() 和 pthread_cleanup_pop() 之间,是一个大个的 do{}while(0), 遇到 return 当然就直接退出啦。 具体的实现方式请看这里, 因为本文只讲述一下线程的创建和退出, 所以 pthread_cleanup_push 和 pthread_cleanup_pop 的说明放在其它地方了。

使用 pthread_cancel() 退出线程

先看一下函数原型:

  1. #include <pthread.h>
  2.  
  3. int pthread_cancel(pthread_t thread);
  4.  
  5. //Compile and link with -pthread.

其中的 thread 参数就是目的线程的线程ID

pthread_cancel() 函数会给 thread 指定的线程发送一个取消请求。 至于目标线程是否以及合适对这个请求进行反应,则视目标线程的两个属性而定: 取消属性的 state 和 type

一个线程的取消属性的 state 由 pthread_setcancelstate(3) 函数来设置, 可以是 enabled (一个新创建的线程的默认方式就是 enabled)或者 disabled。 如果一个线程的取消属性设置了 disabled ,那么对着个线程发送的取消请求会一直存在, 直到线程恢复了取消属性的设置。如果一个线程的取消属性设置了 enabled , 那么取消属性的 type 就由取消消息什么什么时候到来而决定了。

一个线程的取消类型(type)由 pthread_setcanceltype(3) 函数来设置。 可以是异步的,也可以是延缓的。异步取消属性的意味着线程任何时间都可以被取消 (通常是立即被取消,但操作系统不保证这一点)。延缓取消是说,取消操作会被延迟, 直到线程接下来的调用的函数是个取消点。在 pthreads(7) (Linux 命令行中执行 man 7 pthreads) 中列出的函数就是或者是取消点。

当一个取消请求起作用时,下面的步骤会按顺序发生。

    1. 取消清理函数会被出栈并被执行。
    1. 线程相关数据会被析构,顺序不确定。
    1. 线程终止。

以上的步骤会异步的执行,pthread_cancel() 函数的返回状态会指出取消请求是否成功的发给了制定的线程。

在一个被取消的线程终止后,使用 pthread_join(3) 函数 join 时,会得到线程的结束状态为 PTHREAD_CANCELED 。 join 一个线程是知道这个取消操作是否完成的唯一方法。

下面是 man pthread_cancel 手册中的一段示例代码:

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6.  
  7. #define handle_error_en(en, msg) \
  8. do { errno = en; perror(msg); exit(EXIT_FAILURE); } while ()
  9.  
  10. static void *
  11. thread_func(void *ignored_argument)
  12. {
  13. int s;
  14.  
  15. /* Disable cancellation for a while, so that we don't
  16. * immediately react to a cancellation request */
  17.  
  18. s = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
  19. if (s != )
  20. handle_error_en(s, "pthread_setcancelstate");
  21.  
  22. printf("thread_func(): started; cancellation disabled\n");
  23. sleep();
  24. printf("thread_func(): about to enable cancellation\n");
  25.  
  26. s = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  27. if (s != )
  28. handle_error_en(s, "pthread_setcancelstate");
  29.  
  30. /* sleep() is a cancellation point */
  31.  
  32. sleep(); /* Should get canceled while we sleep */
  33.  
  34. /* Should never get here */
  35.  
  36. printf("thread_func(): not canceled!\n");
  37. return NULL;
  38. }
  39.  
  40. int
  41. main(void)
  42. {
  43. pthread_t thr;
  44. void *res;
  45. int s;
  46.  
  47. /* Start a thread and then send it a cancellation request */
  48.  
  49. s = pthread_create(&thr, NULL, &thread_func, NULL);
  50. if (s != )
  51. handle_error_en(s, "pthread_create");
  52.  
  53. sleep(); /* Give thread a chance to get started */
  54.  
  55. printf("main(): sending cancellation request\n");
  56. s = pthread_cancel(thr);
  57. if (s != )
  58. handle_error_en(s, "pthread_cancel");
  59.  
  60. /* Join with thread to see what its exit status was */
  61.  
  62. s = pthread_join(thr, &res);
  63. if (s != )
  64. handle_error_en(s, "pthread_join");
  65.  
  66. if (res == PTHREAD_CANCELED)
  67. printf("main(): thread was canceled\n");
  68. else
  69. printf("main(): thread wasn't canceled (shouldn't happen!)\n");
  70. exit(EXIT_SUCCESS);
  71. }

编译并运行 :

  1. ./pthread_cancel
  2. thread_func(): started; cancellation disabled
  3. main(): sending cancellation request
  4. thread_func(): about to enable cancellation
  5. main(): thread was canceled

同步地址:https://www.fengbohello.top/archives/linux-pthread-lifecycle

POSIX 线程的创建与退出的更多相关文章

  1. Linux多线程编程——线程的创建与退出

    POSIX线程标准:该标准定义了创建和操纵线程的一整套API.在类Unix操作系统(Unix.Linux.Mac OS X等)中,都使用Pthreads作为操作系统的线程.Windows操作系统也有其 ...

  2. LINUX多线程(一)(创建和退出)

    1. Linux多线程概述 1.1. 概述 进程是系统中程序执行和资源分配的基本单位.每个进程有自己的数据段.代码段和堆栈段.这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作.为了进一 ...

  3. Posix线程编程指南(1) 线程创建与取消

    线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列.在串行程序基础上引入线程和进程是为了提高程序的 ...

  4. Win64 驱动内核编程-12.回调监控进线程创建和退出

    回调监控进线程创建和退出 两个注册回调的函数:PsSetCreateProcessNotifyRoutine   进程回调PsSetCreateThreadNotifyRoutine    线程回调分 ...

  5. Linux posix线程库总结

    由于历史原因,2.5.x以前的linux对pthreads没有提供内核级的支持,所以在linux上的pthreads实现只能采用n:1的方式,也称为库实现. 线程的实现,经历了如下发展阶段: Linu ...

  6. Posix线程编程指南(2) 线程私有数据

    概念及作用 在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据.在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有.但有时应用程序设计中有必要提供 ...

  7. POSIX 线程详解 一种支持内存共享的简捷工具

    线程是有趣的 了解如何正确运用线程是每一个优秀程序员必备的素质.线程类似于进程.如同进程,线程由内核按时间分片进行管理.在单处理器系统中,内核使用时间分片来模拟线程的并发执行,这种方式和进程的相同.而 ...

  8. Posix线程编程指南(3) 线程同步

    互斥锁 尽管在Posix Thread中同样可以使用IPC的信号量机制来实现互斥锁mutex功能,但显然semphore的功能过于强大了,在Posix Thread中定义了另外一套专门用于线程同步的m ...

  9. 通用线程:POSIX 线程详解,第 3 部分 条件互斥量(pthread_cond_t)

    使用条件变量提高效率 本文是 POSIX 线程三部曲系列的最后一部分,Daniel 将详细讨论如何使用条件变量.条件变量是 POSIX 线程结构,可以让您在遇到某些条件时“唤醒”线程.可以将它们看作是 ...

随机推荐

  1. miniui表格load数据成功后,回调函数,其中setData要用如下方法

    init: function () { mini.parse(); this.grid = mini.get("jsDatagrid"); var grid1 = mini.get ...

  2. Nowcoder contest 392 I 逛公园 (无向图割边模板)

    <题目链接> 题目描述: 月月和华华一起去逛公园了.公园很大,为了方便,可以抽象的看成一个N个点M条边的无向连通图(点是景点,边是道路).公园唯一的入口在1号点,月月和华华要从这里出发,并 ...

  3. HDU 1257 最少拦截系统 【贪心】

    <题目链接> 题目大意: 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度 ...

  4. UVA 815 Flooded!

    题意:来自:https://blog.csdn.net/lecholin/article/details/70186673 思路: ①数组存每个网格的高度,然后排序,做题时想象为上面的柱状图. ②注意 ...

  5. python3.4爬取网页的乱码问题

    python学习资料文档知识点链接:http://bbs.fishc.com/forum.php?mod=forumdisplay&fid=243&filter=typeid& ...

  6. 潭州课堂25班:Ph201805201 tornado 项目 第三课 项目 图片上传,展示 (课堂笔记)

    tornado 相关说明 f增加图片上传功能, 在 main.py ,文件中创建个  UploadHandler 类,用来处理图片上传 上传图片之前,要先渲染这个页面,所以定个 get 方法,把这个页 ...

  7. 卡在checking installable status

    npm install卡在checking installable status 笔者在使用NPM过程中经常会用到npm install命令,发现有时候会卡在checking installable ...

  8. Mysql建库建用户建表等常用命令

    格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root -p,回车后提示你输 ...

  9. BZOJ4076 : [Wf2014]Maze Reduction

    设$f[i][j][k]$表示从房间$j$的第$k$扇门进去探索不超过$i$步的情况. 对于$0$步的情况,可以用每个房间的度数来表示. 否则可以绕着那个房间走一圈,将所有情况依次hash来表示. 最 ...

  10. Drying [POJ3104] [二分答案]

    背景 每件衣服都有一定单位水分,在不适用烘干器的情况下,每件衣服每分钟自然流失1个单位水分,但如果使用了烘干机则每分钟流失K个单位水分,但是遗憾是只有1台烘干机,每台烘干机同时只能烘干1件衣服,请问要 ...