线程高级编程

线程属性

本章节会对一些很少使用的属性对象及相关函数做详细介绍。

线程属性

RT-Thread实现的线程属性包括线程栈大小、线程优先级、线程分离状态、线程调度策略。pthread_create()使用属性对象前必须先对属性对象进行初始化。设置线程属性之类的API函数应在创建线程之前就调用。线程属性的变更不会影响到已创建的线程。

线程属性结构pthread_attr_t定义在pthread.h头文件里。线程属性结构如下:

  1. /* pthread_attr_t 类型重定义*/
  2. typedef struct pthread_attr pthread_attr_t;
  3. /*线程属性结构体*/
  4. struct pthread_attr
  5. {
  6. void* stack_base; /* 线程栈的地址 */
  7. rt_uint32_t stack_size; /* 线程栈大小 */
  8. rt_uint8_t priority; /* 线程优先级 */
  9. rt_uint8_t detachstate; /*线程的分离状态 */
  10. rt_uint8_t policy; /* 线程调度策略 */
  11. rt_uint8_t inheritsched; /* 线程的继承性 */
  12. };

线程属性初始化及去初始化

函数原型

  1. int pthread_attr_init(pthread_attr_t *attr);
  2. int pthread_attr_destroy(pthread_attr_t *attr);

  1. 参数 描述

  1. attr 指向线程属性的指针

函数功能

初始化线程属性 \ 对线程属性去初始化。

函数返回

2个函数只返回0值,总是成功。

使用pthread_attr_init()函数会使用默认值初始化线程属性结构体attr,等同于调用线程初始化函数时将此参数设置为NULL,使用前需要定义一个pthread_attr_t属性对象,此函数必须在pthread_create()函数之前调用。

pthread_attr_destroy()函数对attr指向的属性去初始化,之后可以再次调用 pthread_attr_init()函数对此属性对象重新初始化。

线程的分离状态

函数原型

  1. int pthread_attr_setdetachstate(pthread_attr_t *attr, int state);
  2. int pthread_attr_getdetachstate(pthread_attr_t const *attr, int *state);

  1. 参数 描述

  1. attr 指向线程属性的指针
  2.  
  3. state 线程分离状态

函数功能

设置线程的分离状态 / 获取线程的分离状态,默认情况下线程是非分离状态。

函数返回

2个函数只返回0值,总是成功。

线程分离状态属性值state可以是PTHREAD_CREATE_JOINABL(非分离)和 PTHREAD_CREATE_DETACHED(分离)。

线程的分离状态决定一个线程以什么样的方式来回收自己运行结束后占用的资源。线程的分离状态有2种:joinable或者detached。当线程创建后,应该调用pthread_join()或者pthread_detach()回收线程结束运行后占用的资源。如果线程的分离状态为joinable其他线程可以调用pthread_join()函数等待该线程结束并获取线程返回值,然后回收线程占用的资源。分离状态为detached的线程不能被其他的线程所join,自己运行结束后,马上释放系统资源。

线程的调度策略

函数原型

  1. int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
  2. int pthread_attr_getschedpolicy(pthread_attr_t const *attr, int *policy);

只实现了函数接口,默认不同优先级基于优先级调度,同一优先级时间片轮询调度

线程的调度参数

函数原型

  1. int pthread_attr_setschedparam(pthread_attr_t *attr,
  2. struct sched_param const *param);
  3. int pthread_attr_getschedparam(pthread_attr_t const *attr,
  4. struct sched_param *param);

  1. 参数 描述

  1. attr 指向线程属性的指针
  2.  
  3. param 指向调度参数的指针

函数功能

设置线程的优先级 / 获取线程的优先级。

函数返回

2个函数只返回0值,总是成功。

pthread_attr_setschedparam()函数设置线程的优先级。使用param对线程属性优先级赋值。

参数struct sched_param定义在sched.h里,结构如下:

  1. struct sched_param
  2. {
  3. int sched_priority; /* 线程优先级 */
  4. };

结构体sched_param的成员sched_priority控制线程的优先级值。

线程的堆栈大小

函数原型

  1. int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size);
  2. int pthread_attr_getstacksize(pthread_attr_t const *attr, size_t *stack_size);

  1. 参数 描述

  1. attr 指向线程属性的指针
  2.  
  3. stack_size 线程堆栈大小

函数功能

设置 / 获取 线程的堆栈大小。

函数返回

2个函数只返回0值,总是成功。

pthread_attr_setstacksize()函数可以设置堆栈大小,单位是字节。在大多数系统中需要做栈空间地址对齐(例如ARM体系结构中需要向4字节地址对齐)。

线程堆栈大小和地址

函数原型

  1. int pthread_attr_setstack(pthread_attr_t *attr,
  2. void *stack_base,
  3. size_t stack_size);
  4. int pthread_attr_getstack(pthread_attr_t const *attr,
  5. void **stack_base,
  6. size_t *stack_size);

  1. 参数 描述

  1. attr 指向线程属性的指针
  2.  
  3. stack_size 线程堆栈大小
  4.  
  5. stack_base 线程堆栈地址

函数功能

设置 / 获取 线程的堆栈地址和堆栈大小。

函数返回

2个函数只返回0值,总是成功。

线程属性相关桩函数

函数原型

  1. int pthread_attr_setscope(pthread_attr_t *attr, int scope);

函数功能

设置线程的作用域。

函数返回

scope 为 PTHREAD_SCOPE_SYSTEM则返回0,scope 为 PTHREAD_SCOPE_PROCESS则返回EOPNOTSUPP,scope为其他值则返回EINVAL。

函数原型

  1. int pthread_attr_getscope(pthread_attr_t const *attr);

函数功能

获取线程的作用域。

函数返回

返回PTHREAD_SCOPE_SYSTEM。

函数原型

  1. int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stack_addr);
  2. int pthread_attr_getstackaddr(pthread_attr_t const *attr, void **stack_addr);

函数功能

设置线程的堆栈地址 / 获取线程的堆栈地址。

函数返回

2个函数都返回EOPNOTSUPP。

函数原型

  1. int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guard_size);
  2. int pthread_attr_getguardsize(pthread_attr_t const *attr, size_t *guard_size);

函数功能

设置线程的警戒缓冲区 / 获取线程的警戒缓冲区。

函数返回

2个函数都返回EOPNOTSUPP。

线程属性示例代码

这个程序会初始化2个线程,它们拥有共同的入口函数,但是它们的入口参数不相同。最先创建的线程会使用提供的attr线程属性,另外一个线程使用系统默认的属性。线程的优先级是很重要的一个参数,因此这个程序会修改第一个创建的线程的优先级为8,而系统默认的优先级为24。

  1. #include <pthread.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <sched.h>
  5.  
  6. /* 线程控制块 */
  7. static pthread_t tid1;
  8. static pthread_t tid2;
  9.  
  10. /* 函数返回值检查 */
  11. static void check_result(char* str,int result)
  12. {
  13. if (0 == result)
  14. {
  15. printf("%s successfully!\n",str);
  16. }
  17. else
  18. {
  19. printf("%s failed! error code is %d\n",str,result);
  20. }
  21. }
  22. /* 线程入口函数*/
  23. static void* thread_entry(void* parameter)
  24. {
  25. int count = 0;
  26. int no = (int) parameter; /* 获得线程的入口参数 */
  27.  
  28. while (1)
  29. {
  30. /* 打印输出线程计数值 */
  31. printf("thread%d count: %d\n", no, count ++);
  32.  
  33. sleep(2); /* 休眠2秒 */
  34. }
  35. }
  36.  
  37. /* 用户应用入口 */
  38. int rt_application_init()
  39. {
  40. int result;
  41. pthread_attr_t attr; /* 线程属性 */
  42. struct sched_param prio; /* 线程优先级 */
  43.  
  44. prio.sched_priority = 8; /* 优先级设置为8 */
  45. pthread_attr_init(&attr); /* 先使用默认值初始化属性 */
  46. pthread_attr_setschedparam(&attr,&prio); /* 修改属性对应的优先级 */
  47.  
  48. /* 创建线程1,属性为attr,入口函数是thread_entry,入口函数参数是1 */
  49. result = pthread_create(&tid1,&attr,thread_entry,(void*)1);
  50. check_result("thread1 created",result);
  51.  
  52. /* 创建线程2,属性为默认值,入口函数是thread_entry,入口函数参数是2 */
  53. result = pthread_create(&tid2,NULL,thread_entry,(void*)2);
  54. check_result("thread2 created",result);
  55.  
  56. return 0;
  57. }

线程取消

取消是一种让一个线程可以结束其它线程运行的机制。一个线程可以对另一个线程发送一个取消请求。依据设置的不同,目标线程可能会置之不理,可能会立即结束也可能会将它推迟到下一个取消点才结束。

发送取消请求

函数原型

  1. int pthread_cancel(pthread_t thread);

  1. 参数 描述

  1. thread 线程句柄

函数返回

只返回0值,总是成功。

此函数发送取消请求给thread线程。Thread线程是否会对取消请求做出回应以及什么时候做出回应依赖于线程取消的状态及类型。

设置取消状态

函数原型

  1. int pthread_setcancelstate(int state, int *oldstate);

  1. 参数 描述

  1. state 有两种值:PTHREAD_CANCEL_ENABLE:取消使能 PTHREAD_CANCEL_DISABLE:取消不使能(线程创建时的默认值)
  2.  
  3. oldstate 保存原来的取消状态

函数返回

成功返回0,state非PTHREAD_CANCEL_ENABLE或者PTHREAD_CANCEL_DISABLE返回EINVAL。

此函数设置取消状态,由线程自己调用。取消使能的线程将会对取消请求做出反应,而取消没有使能的线程不会对取消请求做出反应。

设置取消类型

函数原型

  1. int pthread_setcanceltype(int type, int *oldtype);

  1. 参数 描述

  1. type 2种值:PTHREAD_CANCEL_DEFFERED:线程收到取消请求后继续运行至下一个取消点再结束。(线程创建时的默认值)PTHREAD_CANCEL_ASYNCHRONOUS:线程立即结束。
  2.  
  3. oldtype 保存原来的取消类型

函数返回

成功返回0,type非PTHREAD_CANCEL_DEFFERED或者PTHREAD_CANCEL_ASYNCHRONOUS返回EINVAL。

此函数设置取消类型,由线程自己调用。

设置取消点

函数原型

  1. void pthread_testcancel(void);

函数返回

此函数没有返回值。

此函数在线程调用的地方创建一个取消点。主要由不包含取消点的线程调用,可以回应取消请求。如果在取消状态处于禁用状态下调用pthread_testcancel(),则该函数不起作用。

取消点

取消点也就是线程接受取消请求后会结束运行的地方,根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()等会引起阻塞的系统调用都是取消点。

RT-Thread包含的所有取消点如下:

  • mq_receive()

  • mq_send()

  • mq_timedreceive()

  • mq_timedsend()

  • msgrcv()

  • msgsnd()

  • msync()

  • pthread_cond_timedwait()

  • pthread_cond_wait()

  • pthread_join()

  • pthread_testcancel()

  • sem_timedwait()

  • sem_wait()

  • pthread_rwlock_rdlock()

  • pthread_rwlock_timedrdlock()

  • pthread_rwlock_timedwrlock()

  • pthread_rwlock_wrlock()

线程取消示例代码

此程序会创建2个线程,线程2开始运行后马上休眠8秒,线程1设置了自己的取消状态和类型,之后在一个无限循环里打印运行计数信息。线程2唤醒后向线程1发送取消请求,线程1收到取消请求后马上结束运行。

  1. #include <pthread.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4.  
  5.  
  6. /* 线程控制块*/
  7. static pthread_t tid1;
  8. static pthread_t tid2;
  9.  
  10. /* 函数返回值检查 */
  11. static void check_result(char* str,int result)
  12. {
  13. if (0 == result)
  14. {
  15. printf("%s successfully!\n",str);
  16. }
  17. else
  18. {
  19. printf("%s failed! error code is %d\n",str,result);
  20. }
  21. }
  22. /* 线程1入口函数 */
  23. static void* thread1_entry(void* parameter)
  24. {
  25. int count = 0;
  26. /* 设置线程1的取消状态使能,取消类型为线程收到取消点后马上结束 */
  27. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  28. pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
  29.  
  30. while(1)
  31. {
  32. /* 打印线程计数值输出 */
  33. printf("thread1 run count: %d\n",count ++);
  34. sleep(2); /* 休眠2秒 */
  35. }
  36. }
  37. /* 线程2入口函数*/
  38. static void* thread2_entry(void* parameter)
  39. {
  40. int count = 0;
  41. sleep(8);
  42. /* 向线程1发送取消请求 */
  43. pthread_cancel(tid1);
  44. /* 阻塞等待线程1运行结束 */
  45. pthread_join(tid1,NULL);
  46. printf("thread1 exited!\n");
  47. /* 线程2打印信息开始输出 */
  48. while(1)
  49. {
  50. /* 打印线程计数值输出 */
  51. printf("thread2 run count: %d\n",count ++);
  52. sleep(2); /* 休眠2秒 */
  53. }
  54. }
  55. /* 用户应用入口 */
  56. int rt_application_init()
  57. {
  58. int result;
  59. /* 创建线程1,属性为默认值,分离状态为默认值joinable,
  60. 入口函数是thread1_entry,入口函数参数为NULL */
  61. result = pthread_create(&tid1,NULL,thread1_entry,NULL);
  62. check_result("thread1 created",result);
  63.  
  64. /* 创建线程2,属性为默认值,分离状态为默认值joinable,
  65. 入口函数是thread2_entry,入口函数参数为NULL */
  66. result = pthread_create(&tid2,NULL,thread2_entry,NULL);
  67. check_result("thread2 created",result);
  68.  
  69. return 0;
  70. }

一次性初始化

函数原型

  1. int pthread_once(pthread_once_t * once_control, void (*init_routine) (void));

  1. 参数 描述

once_control 控制变量