pthread_mutex_init等多线程函数的使用详解

线程按照其调度者可以分为用户级线程和核心级线程两种 

用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持; 
我们常用基本就是用户级线程,所以就只总结一下POSIX提供的用户级线程接口; 
基本线程操作相关的函数: 
1线程的建立结束 
2线程的互斥和同步 
3使用信号量控制线程 
4线程的基本属性配置 

基本线程操作:

函数 说明
pthread_create() 创建线程开始运行相关线程函数,运行结束则线程退出
pthread_eixt() 因为exit()是用来结束进程的,所以则需要使用特定结束线程的函数
pthread_join() 挂起当前线程,用于阻塞式地等待线程结束,如果线程已结束则立即返回,0=成功
pthread_cancel() 发送终止信号给thread线程,成功返回0,但是成功并不意味着thread会终止
pthread_testcancel() 在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求.
pthread_setcancelstate() 设置本线程对Cancel信号的反应
pthread_setcanceltype() 设置取消状态 继续运行至下一个取消点再退出或者是立即执行取消动作
pthread_setcancel() 设置取消状态


互斥与同步机制基本函数

函数 说明
pthread_mutex_init() 互斥锁的初始化
pthread_mutex_lock() 锁定互斥锁,如果尝试锁定已经被上锁的互斥锁则阻塞至可用为止
pthread_mutex_trylock() 非阻塞的锁定互斥锁
pthread_mutex_unlock() 释放互斥锁
pthread_mutex_destory() 互斥锁销毁函数


信号量线程控制(默认无名信号量)

函数 说明
sem_init(sem) 初始化一个定位在sem的匿名信号量
sem_wait() 把信号量减1操作,如果信号量的当前值为0则进入阻塞,为原子操作
sem_trywait() 如果信号量的当前值为0则返回错误而不是阻塞调用(errno=EAGAIN),其实是sem_wait()的非阻塞版本
sem_post() 给信号量的值加1,它是一个“原子操作”,即同时对同一个信号量做加1,操作的两个线程是不会冲突的
sem_getvalue(sval) 把sem指向的信号量当前值放置在sval指向的整数上
sem_destory(sem) 销毁由sem指向的匿名信号量


线程属性配置相关函数

函数 说明
pthread_attr_init() 初始化配置一个线程对象的属性,需要用pthread_attr_destroy函数去除已有属性
pthread_attr_setscope() 设置线程属性
pthread_attr_setschedparam() 设置线程优先级
pthread_attr_getschedparam() 获取线程优先级

基本的线程建立运行pthread_create

 
 
  1. /* thread.c */

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <pthread.h>

  5.  
  6. #define THREAD_NUMBER 3 /*线程数*/

  7. #define REPEAT_NUMBER 5 /*每个线程中的小任务数*/

  8. #define DELAY_TIME_LEVELS 10.0 /*小任务之间的最大时间间隔*/

  9. //

  10. void *thrd_func(void *arg) {

  11. /* 线程函数例程 */

  12. int thrd_num = (int)arg;

  13. int delay_time = 0;

  14. int count = 0;

  15. printf("Thread %d is starting\n", thrd_num);

  16. for (count = 0; count < REPEAT_NUMBER; count++) {

  17. delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

  18. sleep(delay_time);

  19. printf("\tThread %d: job %d delay = %d\n",

  20. thrd_num, count, delay_time);

  21. }

  22.  
  23. printf("Thread %d finished\n", thrd_num);

  24. pthread_exit(NULL);

  25. }

  26.  
  27. int main(void) {

  28. pthread_t thread[THREAD_NUMBER];

  29. int no = 0, res;

  30. void * thrd_ret;

  31. srand(time(NULL));

  32. for (no = 0; no < THREAD_NUMBER; no++) {

  33. /* 创建多线程 */

  34. res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);

  35. if (res != 0) {

  36. printf("Create thread %d failed\n", no);

  37. exit(res);

  38. }

  39. }

  40.  
  41. printf("Create treads success\n Waiting for threads to finish...\n");

  42. for (no = 0; no < THREAD_NUMBER; no++) {

  43. /* 等待线程结束 */

  44. res = pthread_join(thread[no], &thrd_ret);

  45. if (!res) {

  46. printf("Thread %d joined\n", no);

  47. } else {

  48. printf("Thread %d join failed\n", no);

  49. }

  50. }

  51. return 0;

  52. }


 

  • 例程中循环3次建立3条线程,并且使用pthread_join函数依次等待线程结束; 

线程中使用rand()获取随机值随机休眠5次,随意会出现后执行的线程先执行完成; 
运行结果:

 
 
  1. $ gcc thread.c -lpthread

  2. $ ./a.out

  3. Create treads success

  4. Waiting for threads to finish...

  5. Thread 0 is starting

  6. Thread 1 is starting

  7. Thread 2 is starting

  8. Thread 1: job 0 delay = 2

  9. Thread 1: job 1 delay = 2

  10. Thread 0: job 0 delay = 8

  11. Thread 2: job 0 delay = 10

  12. Thread 2: job 1 delay = 3

  13. Thread 1: job 2 delay = 10

  14. Thread 0: job 1 delay = 8

  15. Thread 0: job 2 delay = 3

  16. Thread 0: job 3 delay = 1

  17. Thread 2: job 2 delay = 8

  18. Thread 1: job 3 delay = 8

  19. Thread 1: job 4 delay = 1

  20. Thread 1 finished

  21. Thread 2: job 3 delay = 6

  22. Thread 0: job 4 delay = 7

  23. Thread 0 finished

  24. Thread 0 joined

  25. Thread 1 joined

  26. Thread 2: job 4 delay = 10

  27. Thread 2 finished

  28. Thread 2 joined


 

  • 可以看到,线程1先于线程0执行,但是pthread_join的调用时间顺序,先等待线程0执行; 

由于线程1已经早结束,所以线程0被pthread_join等到的时候,线程1已结束,就在等待到线程1时,直接返回; 
 

线程执行的互斥和同步pthread_mutex_lock

在上面的程序中增加互斥锁

 
 
  1. /*thread_mutex.c*/

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <pthread.h>

  5.  
  6. #define THREAD_NUMBER 3 /* 线程数 */

  7. #define REPEAT_NUMBER 3 /* 每个线程的小任务数 */

  8. #define DELAY_TIME_LEVELS 10.0 /*小任务之间的最大时间间隔*/

  9. pthread_mutex_t mutex;

  10.  
  11. void *thrd_func(void *arg) {

  12. int thrd_num = (int)arg;

  13. int delay_time = 0, count = 0;

  14. int res;

  15. /* 互斥锁上锁 */

  16. res = pthread_mutex_lock(&mutex);

  17. if (res) {

  18. printf("Thread %d lock failed\n", thrd_num);

  19. pthread_exit(NULL);

  20. }

  21. printf("Thread %d is starting\n", thrd_num);

  22. for (count = 0; count < REPEAT_NUMBER; count++) {

  23. delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

  24. sleep(delay_time);

  25. printf("\tThread %d: job %d delay = %d\n",

  26. thrd_num, count, delay_time);

  27. }

  28. printf("Thread %d finished\n", thrd_num);

  29. pthread_exit(NULL);

  30. }

  31.  
  32. int main(void) {

  33. pthread_t thread[THREAD_NUMBER];

  34. int no = 0, res;

  35. void * thrd_ret;

  36.  
  37. srand(time(NULL));

  38. /* 互斥锁初始化 */

  39. pthread_mutex_init(&mutex, NULL);

  40. for (no = 0; no < THREAD_NUMBER; no++) {

  41. res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);

  42. if (res != 0) {

  43. printf("Create thread %d failed\n", no);

  44. exit(res);

  45. }

  46. }

  47. printf("Create treads success\n Waiting for threads to finish...\n");

  48. for (no = 0; no < THREAD_NUMBER; no++) {

  49. res = pthread_join(thread[no], &thrd_ret);

  50. if (!res) {

  51. printf("Thread %d joined\n", no);

  52. } else {

  53. printf("Thread %d join failed\n", no);

  54. }

  55. }

  56. /****互斥锁解锁***/

  57. pthread_mutex_unlock(&mutex);

  58. pthread_mutex_destroy(&mutex);

  59. return 0;

  60. }


 

  • 在上面的例程中直接添加同步锁pthread_mutex_t; 

在线程中加入,于是程序在执行线程程序时; 
调用pthread_mutex_lock上锁,发现上锁时候后进入等待,等待锁再次释放后重新上锁; 
所以线程程序加载到队列中等待,等待成功上锁后继续执行程序代码; 
运行结果

 
 
  1. $gcc thread_mutex.c -lpthread

  2. $ ./a.out

  3. Create treads success

  4. Waiting for threads to finish...

  5. Thread 0 is starting

  6. Thread 0: job 0 delay = 9

  7. Thread 0: job 1 delay = 4

  8. Thread 0: job 2 delay = 7

  9. Thread 0 finished

  10. Thread 0 joined

  11. Thread 1 is starting

  12. Thread 1: job 0 delay = 6

  13. Thread 1: job 1 delay = 4

  14. Thread 1: job 2 delay = 7

  15. Thread 1 finished

  16. Thread 1 joined

  17. Thread 2 is starting

  18. Thread 2: job 0 delay = 3

  19. Thread 2: job 1 delay = 1

  20. Thread 2: job 2 delay = 6

  21. Thread 2 finished

  22. Thread 2 joined


 

  • 跟例程1中执行结果不同,线程程序被加载到队列中而不能马上执行,需要等到能够成功上锁; 

上锁后,继续执行线程程序,上锁执行; 
这样线程被依次执行的情况在实际使用场景中经常出现; 
使用场景: 
当用户登录后获取秘钥才能继续获取该用户的基本信息时;需要等待登录线程结束后才能继续执行获取用户信息的线程时, 
需要调用两条线程,假如是:threadLogin(),threadGetInfo(); 则可以有2种方法实现: 
1 此时可以使用互斥锁同时一次性调用完threadLogin()和threadGetInfo(); 
2 当然也可以不使用互斥锁直接在threadLogin()中登录验证成功后调用threadGetInfo(); 
相比之下,方式1更加清晰的显示逻辑关系,增加代码可读性可扩展性。

使用信号量控制线程的执行顺序sem_post

修改上面例程,上面的是使用pthread_mutex_lock互斥锁控制线程执行顺序, 
使用另外一种线程执行顺序的控制;

 
 
  1. /* thread_sem.c */

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <pthread.h>

  5. #include <semaphore.h>

  6.  
  7. #define THREAD_NUMBER 3

  8. #define REPEAT_NUMBER 3

  9. #define DELAY_TIME_LEVELS 10.0

  10.  
  11. sem_t sem[THREAD_NUMBER];

  12.  
  13. void * thrd_func(void *arg) {

  14. int thrd_num = (int)arg;

  15. int delay_time = 0;

  16. int count = 0;

  17. sem_wait(&sem[thrd_num]);

  18. printf("Thread %d is starting\n", thrd_num);

  19. for (count = 0; count < REPEAT_NUMBER; count++) {

  20. delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

  21. sleep(delay_time);

  22. printf("\tThread %d: job %d delay = %d\n", thrd_num, count, delay_time);

  23. }

  24. printf("Thread %d finished\n", thrd_num);

  25. pthread_exit(NULL);

  26. }

  27.  
  28. int main(void) {

  29. pthread_t thread[THREAD_NUMBER];

  30. int no = 0, res;

  31. void * thrd_ret;

  32. srand(time(NULL));

  33. for (no = 0; no < THREAD_NUMBER; no++) {

  34. sem_init(&sem[no], 0, 0);

  35. res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);

  36. if (res != 0) {

  37. printf("Create thread %d failed\n", no);

  38. exit(res);

  39. }

  40. }

  41.  
  42. printf("Create treads success\n Waiting for threads to finish...\n");

  43. sem_post(&sem[THREAD_NUMBER - 1]);

  44. for (no = THREAD_NUMBER - 1; no >= 0; no--) {

  45. res = pthread_join(thread[no], &thrd_ret);

  46. if (!res) {

  47. printf("Thread %d joined\n", no);

  48. } else {

  49. printf("Thread %d join failed\n", no);

  50. }

  51. sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]);

  52. }

  53.  
  54. for (no = 0; no < THREAD_NUMBER; no++) {

  55. sem_destroy(&sem[no]);

  56. }

  57. return 0;

  58. }


 

  •  

执行结果,仍然是建立3条线程,每条线程执行时休眠随机时长:

 
 
  1. $ gcc thread_sem.c -lpthread

  2. $ ./a.out

  3. Create treads success

  4. Waiting for threads to finish...

  5. Thread 2 is starting

  6. Thread 2: job 0 delay = 9

  7. Thread 2: job 1 delay = 9

  8. Thread 2: job 2 delay = 5

  9. Thread 2 finished

  10. Thread 2 joined

  11. Thread 1 is starting

  12. Thread 1: job 0 delay = 5

  13. Thread 1: job 1 delay = 7

  14. Thread 1: job 2 delay = 4

  15. Thread 1 finished

  16. Thread 1 joined

  17. Thread 0 is starting

  18. Thread 0: job 0 delay = 3

  19. Thread 0: job 1 delay = 9

  20. Thread 0: job 2 delay = 8

  21. Thread 0 finished

  22. Thread 0 joined


 

  •  

执行结果与第2个例程非常相似,只不过教材中进行倒序执行而已; 
那么这种方式其实与使用互斥锁相比,代码量可读性基本持平不相上下;

线程的基本属性pthread_attr_setscope

设置属性一般有: 
1 绑定属性 
2 分离属性 
3 堆栈地址 
4 堆栈大小 
5 优先级

关于绑定属性就是绑定于内核线程; 
分离属性主要是讲线程结束后是否马上释放相应的内存;

 
 
  1. /* thread_attr.c */

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. #include <pthread.h>

  5.  
  6. #define THREAD_NUMBER 1

  7. #define REPEAT_NUMBER 3

  8. #define DELAY_TIME_LEVELS 10.0

  9. int finish_flag = 0;

  10.  
  11. void * thrd_func(void * arg){

  12. int delay_time = 0;

  13. int count = 0;

  14. printf("Thread is starting\n");

  15. for (count = 0; count < REPEAT_NUMBER; count++) {

  16. delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

  17. sleep(delay_time);

  18. printf("\tThread : job %d delay = %d\n", count, delay_time);

  19. }

  20. printf("Thread finished\n");

  21. finish_flag = 1;

  22. pthread_exit(NULL);

  23. }

  24.  
  25. int main(void) {

  26. pthread_t thread;

  27. pthread_attr_t attr;

  28. int res = 0;

  29. srand(time(NULL));

  30. res = pthread_attr_init(&attr);

  31. if (res != 0) {

  32. printf("Create attribute failed\n");

  33. exit(res);

  34. }

  35. res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

  36. res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  37. if (res != 0) {

  38. printf("Setting attribute failed\n");

  39. exit(res);

  40. }

  41. res = pthread_create(&thread, &attr, thrd_func, NULL);

  42. if (res != 0) {

  43. printf("Create thread failed\n");

  44. exit(res);

  45. }

  46. pthread_attr_destroy(&attr);

  47. printf("Create tread success\n");

  48.  
  49. while(!finish_flag){

  50. printf("Waiting for thread to finish...\n");

  51. sleep(2);

  52. }

  53. return 0;

  54. }


 

  •  

在运行前后使用 $ free 命令查看内存前后的使用情况发现: 
在线程结束后内存马上被释放; 
其实,一般线程的属性直接使用系统默认属性即可; 
关于线程的使用,基本如此; 
所有例程来自:《嵌入式Linux应用程序开发标准教程》

发布了136 篇原创文章 · 获赞 71 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/u012308586/article/details/104635618
今日推荐