互斥量、条件变量与pthread_cond_wait()函数的使用,详解(二)

1.Linux“线程”

进程与线程之间是有区别的,不过linux内核只提供了轻量进程的支持,未实现线程模型。Linux是一种“多进程单线程”的操作系统。Linux本身只有进程的概念,而其所谓的“线程”本质上在内核里仍然是进程。

大家知道,进程是资源分配的单位,同一进程中的多个线程共享该进程的资源(如作为共享内存的全局变量)。Linux中所谓的“线程”只是在被创建时clone了父进程的资源,因此clone出来的进程表现为“线程”,这一点一定要弄清楚。因此,Linux“线程”这个概念只有在打冒号的情况下才是最准确的。

目前Linux中最流行的线程机制为LinuxThreads,所采用的就是线程-进程“一对一”模型,调度交给核心,而在用户级实现一个包括信号处理在内的线程管理机制。LinuxThreads由Xavier Leroy (Xavier.Leroy@inria.fr)负责开发完成,并已绑定在GLIBC中发行,它实现了一种BiCapitalized面向Linux的Posix 1003.1c “pthread”标准接口。Linuxthread可以支持Intel、Alpha、MIPS等平台上的多处理器系统。

按照POSIX 1003.1c 标准编写的程序与Linuxthread 库相链接即可支持Linux平台上的多线程,在程序中需包含头文件pthread. h,在编译链接时使用命令:

gcc -D -REENTRANT -lpthread xxx. c

其中-REENTRANT宏使得相关库函数(如stdio.h、errno.h中函数) 是可重入的、线程安全的(thread-safe),-lpthread则意味着链接库目录下的libpthread.a或libpthread.so文件。使用Linuxthread库需要2.0以上版本的Linux内核及相应版本的C库(libc 5.2.18、libc 5.4.12、libc 6)。

2.“线程”控制

线程创建

进程被创建时,系统会为其创建一个主线程,而要在进程中创建新的线程,则可以调用pthread_create:

pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * 
(start_routine)(void*), void *arg);

start_routine为新线程的入口函数,arg为传递给start_routine的参数。

每个线程都有自己的线程ID,以便在进程内区分。线程ID在pthread_create调用时回返给创建线程的调用者;一个线程也可以在创建后使用pthread_self()调用获取自己的线程ID:

pthread_self (void) ;

线程退出

线程的退出方式有三:

(1)执行完成后隐式退出;

(2)由线程本身显示调用pthread_exit 函数退出;

pthread_exit (void * retval) ;

(3)被其他线程用pthread_cance函数终止:

pthread_cance (pthread_t thread) ;

在某线程中调用此函数,可以终止由参数thread 指定的线程。

如果一个线程要等待另一个线程的终止,可以使用pthread_join函数,该函数的作用是调用pthread_join的线程将被挂起直到线程ID为参数thread的线程终止:

pthread_join (pthread_t thread, void** threadreturn);

3.线程通信 

线程互斥

互斥意味着“排它”,即两个线程不能同时进入被互斥保护的代码。Linux下可以通过pthread_mutex_t 定义互斥体机制完成多线程的互斥操作,该机制的作用是对某个需要互斥的部分,在进入时先得到互斥体,如果没有得到互斥体,表明互斥部分被其它线程拥有,此时欲获取互斥体的线程阻塞,直到拥有该互斥体的线程完成互斥部分的操作为止。

下面的代码实现了对共享全局变量x 用互斥体mutex 进行保护的目的:

int x; // 进程中的全局变量 
pthread_mutex_t mutex; 
pthread_mutex_init(&mutex, NULL); //按缺省的属性初始化互斥体变量mutex 
pthread_mutex_lock(&mutex); // 给互斥体变量加锁 
… //对变量x 的操作 
phtread_mutex_unlock(&mutex); // 给互斥体变量解除锁

线程同步

同步就是线程等待某个事件的发生。只有当等待的事件发生线程才继续执行,否则线程挂起并放弃处理器。当多个线程协作时,相互作用的任务必须在一定的条件下同步。

Linux下的C语言编程有多种线程同步机制,最典型的是条件变量(condition variable)。pthread_cond_init用来创建一个条件变量,其函数原型为:

pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);

pthread_cond_wait和pthread_cond_timedwait用来等待条件变量被设置,值得注意的是这两个等待调用需要一个已经上锁的互斥体mutex,这是为了防止在真正进入等待状态之前别的线程有可能设置该条件变量而产生竞争。pthread_cond_wait的函数原型为:

pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);

pthread_cond_broadcast用于设置条件变量,即使得事件发生,这样等待该事件的线程将不再阻塞:

pthread_cond_broadcast (pthread_cond_t *cond) ;

pthread_cond_signal则用于解除某一个等待线程的阻塞状态:

pthread_cond_signal (pthread_cond_t *cond) ;

pthread_cond_destroy 则用于释放一个条件变量的资源。

在头文件semaphore.h 中定义的信号量则完成了互斥体和条件变量的封装,按照多线程程序设计中访问控制机制,控制对资源的同步访问,提供程序设计人员更方便的调用接口。

sem_init(sem_t *sem, int pshared, unsigned int val);

这个函数初始化一个信号量sem 的值为val,参数pshared 是共享属性控制,表明是否在进程间共享。

sem_wait(sem_t *sem);

调用该函数时,若sem为无状态,调用线程阻塞,等待信号量sem值增加(post )成为有信号状态;若sem为有状态,调用线程顺序执行,但信号量的值减一。

sem_post(sem_t *sem);

调用该函数,信号量sem的值增加,可以从无信号状态变为有信号状态。

4.实例 

下面我们还是以名的生产者/消费者问题为例来阐述Linux线程的控制和通信。一组生产者线程与一组消费者线程通过缓冲区发生联系。生产者线程将生产的产品送入缓冲区,消费者线程则从中取出产品。缓冲区有N 个,是一个环形的缓冲池。

[cpp] view plain copy

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #define BUFFER_SIZE 16 // 缓冲区数量
  4. struct prodcons
  5. {
  6. // 缓冲区相关数据结构
  7. int buffer[BUFFER_SIZE]; /* 实际数据存放的数组*/
  8. pthread_mutex_t lock; /* 互斥体lock 用于对缓冲区的互斥操作 */
  9. int readpos, writepos; /* 读写指针*/
  10. pthread_cond_t notempty; /* 缓冲区非空的条件变量 */
  11. pthread_cond_t notfull; /* 缓冲区未满的条件变量 */
  12. };
  13. /* 初始化缓冲区结构 */
  14. void init(struct prodcons *b)
  15. {
  16. pthread_mutex_init(&b->lock, NULL);
  17. pthread_cond_init(&b->notempty, NULL);
  18. pthread_cond_init(&b->notfull, NULL);
  19. b->readpos = 0;
  20. b->writepos = 0;
  21. }
  22. /* 将产品放入缓冲区,这里是存入一个整数*/
  23. void put(struct prodcons *b, int data)
  24. {
  25. pthread_mutex_lock(&b->lock);
  26. /* 等待缓冲区未满*/
  27. if ((b->writepos + 1) % BUFFER_SIZE == b->readpos)
  28. {
  29. pthread_cond_wait(&b->notfull, &b->lock);
  30. }
  31. /* 写数据,并移动指针 */
  32. b->buffer[b->writepos] = data;
  33. b->writepos++;
  34. if (b->writepos >= BUFFER_SIZE)
  35. b->writepos = 0;
  36. /* 设置缓冲区非空的条件变量*/
  37. pthread_cond_signal(&b->notempty);
  38. pthread_mutex_unlock(&b->lock);
  39. }
  40. /* 从缓冲区中取出整数*/
  41. int get(struct prodcons *b)
  42. {
  43. int data;
  44. pthread_mutex_lock(&b->lock);
  45. /* 等待缓冲区非空*/
  46. if (b->writepos == b->readpos)
  47. {
  48. pthread_cond_wait(&b->notempty, &b->lock);
  49. }
  50. /* 读数据,移动读指针*/
  51. data = b->buffer[b->readpos];
  52. b->readpos++;
  53. if (b->readpos >= BUFFER_SIZE)
  54. b->readpos = 0;
  55. /* 设置缓冲区未满的条件变量*/
  56. pthread_cond_signal(&b->notfull);
  57. pthread_mutex_unlock(&b->lock);
  58. return data;
  59. }
  60. /* 测试:生产者线程将1 到10000 的整数送入缓冲区,消费者线
  61. 程从缓冲区中获取整数,两者都打印信息*/
  62. #define OVER ( - 1)
  63. struct prodcons buffer;
  64. void *producer(void *data)
  65. {
  66. int n;
  67. for (n = 0; n < 10000; n++)
  68. {
  69. printf("%d --->\n", n);
  70. put(&buffer, n);
  71. } put(&buffer, OVER);
  72. return NULL;
  73. }
  74. void *consumer(void *data)
  75. {
  76. int d;
  77. while (1)
  78. {
  79. d = get(&buffer);
  80. if (d == OVER)
  81. break;
  82. printf("--->%d \n", d);
  83. }
  84. return NULL;
  85. }
  86. int main(void)
  87. {
  88. pthread_t th_a, th_b;
  89. void *retval;
  90. init(&buffer);
  91. /* 创建生产者和消费者线程*/
  92. pthread_create(&th_a, NULL, producer, 0);
  93. pthread_create(&th_b, NULL, consumer, 0);
  94. /* 等待两个线程结束*/
  95. pthread_join(th_a, &retval);
  96. pthread_join(th_b, &retval);
  97. return 0;
  98. }

5.WIN32、VxWorks、Linux线程类比

目前为止,笔者已经创作了《基于嵌入式操作系统VxWorks的多任务并发程序设计》(《软件报》2006年5~12期连载)、《深入浅出Win32多线程程序设计》(天极网技术专题)系列,我们来找出这两个系列文章与本文的共通点。

看待技术问题要瞄准其本质,不管是Linux、VxWorks还是WIN32,其涉及到多线程的部分都是那些内容,无非就是线程控制和线程通信,它们的许多函数只是名称不同,其实质含义是等价的,下面我们来列个三大操作系统共同点详细表单:

事项 WIN32 VxWorks Linux
线程创建 CreateThread taskSpawn pthread_create
线程终止 执行完成后退出;线程自身调用ExitThread函数即终止自己;被其他线程调用函数TerminateThread函数 执行完成后退出;由线程本身调用exit退出;被其他线程调用函数taskDelete终止 执行完成后退出;由线程本身调用pthread_exit 退出;被其他线程调用函数pthread_cance终止
获取线程ID GetCurrentThreadId taskIdSelf pthread_self
创建互斥 CreateMutex semMCreate pthread_mutex_init
获取互斥 WaitForSingleObject、WaitForMultipleObjects semTake pthread_mutex_lock
释放互斥 ReleaseMutex semGive phtread_mutex_unlock
创建信号量 CreateSemaphore semBCreate、semCCreate sem_init
等待信号量 WaitForSingleObject semTake sem_wait
释放信号量 ReleaseSemaphore semGive sem_post

6.小结

本章讲述了Linux下多线程的控制及线程间通信编程方法,给出了一个生产者/消费者的实例,并将Linux的多线程与WIN32、VxWorks多线程进行了类比,总结了一般规律。鉴于多线程编程已成为开发并发应用程序的主流方法,学好本章的意义也便不言自明。

[cpp] view plain copy

  1. #include <stdio.h>
  2. #include <stdio.h>
  3. #include <pthread.h>
  4. void thread(void)
  5. {
  6. int i;
  7. for(i=0;i<3;i++)
  8. printf("This is a pthread.\n");
  9. }
  10. int main(void)
  11. {
  12. pthread_t id;
  13. int i,ret;
  14. ret=pthread_create(&id,NULL,(void *) thread,NULL);
  15. if(ret!=0){
  16. printf ("Create pthread error!\n");
  17. exit (1);
  18. }
  19. for(i=0;i<3;i++)
  20. printf("This is the main process.\n");
  21. pthread_join(id,NULL);
  22. return (0);
  23. }

编译:

gcc example1.c -lpthread -o example1

[cpp] view plain copy

  1. #include <pthread.h>
  2. #include <stdio.h>
  3. #include <sys/time.h>
  4. #include <string.h>
  5. #define MAX 10
  6. pthread_t thread[2];
  7. pthread_mutex_t mut;
  8. int number=0, i;
  9. void *thread1()
  10. {
  11. printf ("thread1 : I'm thread 1\n");
  12. for (i = 0; i < MAX; i++)
  13. {
  14. printf("thread1 : number = %d\n",number);
  15. pthread_mutex_lock(&mut);
  16. number++;
  17. pthread_mutex_unlock(&mut);
  18. sleep(2);
  19. }
  20. printf("thread1 :主函数在等我完成任务吗?\n");
  21. pthread_exit(NULL);
  22. }
  23. void *thread2()
  24. {
  25. printf("thread2 : I'm thread 2\n");
  26. for (i = 0; i < MAX; i++)
  27. {
  28. printf("thread2 : number = %d\n",number);
  29. pthread_mutex_lock(&mut);
  30. number++;
  31. pthread_mutex_unlock(&mut);
  32. sleep(3);
  33. }
  34. printf("thread2 :主函数在等我完成任务吗?\n");
  35. pthread_exit(NULL);
  36. }
  37. void thread_create(void)
  38. {
  39. int temp;
  40. memset(&thread, 0, sizeof(thread)); //comment1
  41. //创建线程
  42. if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0) //comment2
  43. printf("线程1创建失败!\n");
  44. else
  45. printf("线程1被创建\n");
  46. if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0) //comment3
  47. printf("线程2创建失败");
  48. else
  49. printf("线程2被创建\n");
  50. }
  51. void thread_wait(void)
  52. {
  53. //等待线程结束
  54. if(thread[0] !=0) { //comment4
  55. pthread_join(thread[0],NULL);
  56. printf("线程1已经结束\n");
  57. }
  58. if(thread[1] !=0) { //comment5
  59. pthread_join(thread[1],NULL);
  60. printf("线程2已经结束\n");
  61. }
  62. }
  63. int main()
  64. {
  65. //用默认属性初始化互斥锁
  66. pthread_mutex_init(&mut,NULL);
  67. printf("我是主函数哦,我正在创建线程,呵呵\n");
  68. thread_create();
  69. printf("我是主函数哦,我正在等待线程完成任务阿,呵呵\n");
  70. thread_wait();
  71. return 0;
  72. }

编译 :

gcc -lpthread -o thread_example lp.c

转自http://www.cnblogs.com/BiffoLee/archive/2011/11/18/2254540.html

作者:怀想天空

出处:http://www.cnblogs.com/cyyljw/ 
本博客文章,大多系网络中收集,转载请注明出处 
相关标签:嵌入式开发、嵌入式学习

互斥量、条件变量与pthread_cond_wait()函数的使用,详解(二)相关推荐

  1. 信号量 互斥量 条件变量

    原文:https://blog.csdn.net/qq_32646795/article/details/78221005 本文打算写一些和锁有关的东西,谈一谈我对锁的原理和实现的理解,主要包含以下方 ...

  2. pthread和互斥量条件变量函数意义速查表

    数据类型 pthread_t 线程 互斥量和条件变量

  3. python def函数报错详解_Python函数详解

    一.Python中函数的概念 1.python中函数概念 Python中函数是逻辑结构化和过程化的一种编程方法. 2.python中函数定义方法解释 def name(a): "The fu ...

  4. python decode函数的用法_Oracle DECODE函数的用法详解

    Oracle DECODE函数 使用方法: 1.比较大小 select decode(sign(变量1-变量2),-1,变量1,变量2) from dual; --取较小值 sign()函数根据某个值 ...

  5. 【Python养成】常用内置函数 — 2(详解25个内置函数)

    图片来自互联网 文章目录 前言 二.内置函数详解 1.函数:chr(x) 2.函数:dir([obj]) 3.函数:divmod(x,y) 4.函数:enumerate(sequence, [star ...

  6. 利用栈求表达式的值_高一数学月考考点之函数的表达式详解

    函数表达式考点详解,教你轻松学函数 Hello,大家好,这里是摆渡学涯.很高兴在这里跟大家分享知识哦.这次课程我们来为大家讲一下函数表达式相关的考点,教你轻松学函数. 基本概念 什么是函数表达式呢?我 ...

  7. python时间函数报错_python3中datetime库,time库以及pandas中的时间函数区别与详解...

    1介绍datetime库之前 我们先比较下time库和datetime库的区别 先说下time 在 Python 文档里,time是归类在Generic Operating System Servic ...

  8. countif和sum套用_if函数嵌套countif、sumif函数实例操作详解 你需要学习了

    excel中if函数嵌套countif函数实例操作详解,需要了解if函数.countif函数.sumif函数字符串引用方式,isblank函数 操作方法 01 用扫描枪连接excel后将自动将货物名称 ...

  9. python获取系统时间函数_python3中datetime库,time库以及pandas中的时间函数区别与详解...

    1介绍datetime库之前 我们先比较下time库和datetime库的区别 先说下time 在 Python 文档里,time是归类在Generic Operating System Servic ...

最新文章

  1. redis源码分析(beta版本)-redis实现的概述逻辑
  2. 使用jQuery设置disabled属性与移除disabled属性
  3. python PyQt5.QtWidgets.QWidget类
  4. keil obj 文件 结构_OBJ文件格式详解
  5. mysql 主从 keepalived_一次mysql主从加keepalived配置搭建及切换演示
  6. linux QT 结束当前进程_Linux常用使用命令梳理
  7. ps cs3怎样能保存html,ps cs3用消失点清理杂物方法介绍
  8. 基于socket的C语言编程,C语言实现的Socket编程
  9. Android 开发者们,如何使用 Python 来扩展 adb 命令?
  10. iOS Xcode 调试 Unable to fix code signing issue
  11. 喜马拉雅下载成mp3方法
  12. Linux 远程拷贝命令
  13. stata 导出 相关系数表_STATA数据处理技巧与计量分析二|基本语句介绍
  14. 成功必须靠自己去争取。
  15. 智云影视资源网PHP源码 采集无需数据库V1.2版
  16. 高中物理应用计算机教学心得,高中物理个人教学总结(通用3篇)
  17. (前端)HTML之CSS(选择器字体排版背景)
  18. Oracle中有dateadd吗,oracle dateadd的函数有哪些?
  19. 常见交通工具英语单词
  20. 网络加速器是干什么用的?

热门文章

  1. abstract关键字的使用
  2. 特别行动队[斜率优化]
  3. ctl文件去空格_CTL文件用法
  4. 对人工智能大数据领域的创业,主要有哪些好的建议
  5. html 弹出层 边框半透明,js+CSS实现弹出居中背景半透明div层的方法
  6. FFA 2022 专场解读 - 流批一体 平台建设 AI 特征工程
  7. 管状合金电阻和片状合金电阻的区别_R007合金电阻 电动滑板车专用电阻 大毅现货供应...
  8. 鼠标光标变成黑块变粗导致影响输入的原因以及解决办法
  9. 用java –jar 命令运行Jar包
  10. getRealPath详细解释