Linux多线程通信及同步_利用对列在两个线程之间通信-程序员宅基地

技术标签: 工作学习  

建立多任务模型,并用线程来实现 

  符合POSIX标准的UNIX操作系统提供了线程的控制函数,如:线程的创建和终止、线程之间的互斥、线程之间的同步等。利用这些系统函数可以成功地模拟消息队列,来实现线程间数据共享和同步,以完成多任务的实时性。为成功地描述线程间数据共享和同步,以下列任务模型为例。 

  首先建立消息队列属性数据结构 

   #define MAXQUEUE 30 

   typedef struct mq_attrib { 

   char name[20]; 

   pthread_mutex_t mutex_buff; 

   pthread_mutex_t mutex_cond; 

   pthread cond_t cond; 

   int maxElements; 

   int elementLength; 

   int curElementNum; 

   caddr_t buff; 

  }mq_attrib,mq_attribstruct,mq_attrib_t; 

   mq_attrib_t msqueue[MAXQUEUE]; 

  数据结构定义了消息队列的名字name,最大消息个数maxElements,单个消息长度elementLength,当前消息个数curElementNum,存放消息的缓冲区buff,保护缓冲区锁mutex_buff,线程同步条件变量cond,保护线程同步条件变量锁mutex_cond。 

  消息队列的创建 

  依据此数据结构进行消息队列的创建,函数为msqueue_create(参数解释:name消息队列名,maxnum消息的最大个数,length单个消息的长度)。 

  int msqueue_create( name, maxnum, length ) 

  charname; 

  int maxnum,length; 

  { 

   int i; 

   for ( i=0; i 

   if ( msqueue[i]==NULL )break; 

   //如果消息队列全部被分配,返回错 

   if ( i==MAXQUEUE ) return MQERROR; 

   msqueue[i]=malloc(sizeof(mq_attribstruct)); 

   sprintf( msqueue[i]->name, "%s", name); 

   msqueue[i]->maxElements = maxnum; 

   msqueue[i]->elementLength = length; 

   msqueue[i]->curElementNum = 0; 

   msqueue[i]->buff=malloc(maxnumlength); 

   //对保护锁进行初始化 

   pthread_mutex_init(&&msqueue[i] 

  ->mutex_buff, NULL); 

   pthread_mutex_init(&&msqueue[i] 

  ->mutex_cond, NULL); 

   //对线程同步条件变量初始化 

   pthread_cond_init(&&msqueue[i]->cond, NULL); 

   return i; 

  } 

  应用消息队列进行消息的发送和接收 

  发送消息到消息队列: 

  消息队列的发送和接收是在不同的线程中进行的。首先介绍发送消息到消息队列的函数: 

  int msqueue_send ( id, buff, length ) 

  int id, length; 

  caddr_t buff; 

  { 

   int pos; 

   //消息队列id错,返回错 

   if ( id<0 || id >= MAXQUEU ) return MQERROR; 

   //消息长度与创建时的长度不符,返回错 

   if ( length != msqueue[id]->elementLength ) return MQERROR; 

   //消息队列满,不能发送 

   if ( msqueue[id]->curElementNum >= msqueue[id]->maxElements ) 

   return MQERROR; 

   //在对消息队列缓冲区操作前,锁住缓冲区,以免其他线程操作 

   pthread_mutex_lock ( &&msqueue[id]->mutex_buff ); 

   pos = msqueue[id]->curElementNum * msqueue[id]->elementLength; 

   bcopy ( buff, &&msqueue[id]->buff[pos], msqueue[id]->elementLength ); 

   msqueue[id]->curElementNum ++; 

   pthread_mutex_unlock ( &&msqueue[id]->mutex_buff ); 

   //如果插入消息前,消息队列是空的,插入消息后,消息队列为非空,则通知等待从消息队列取消息的线程,条件满足,可以取出消息进行处理 

   if ( msqueue[id]->curElementNum == 1 ) { 

   pthread_mutex_lock ( &&msqueue[id]->mutex_cond ); 

   pthread_cond_broadcast ( &&msqueue[id]->cond ); 

   pthread_mutex_unlock ( &&msqueue[id]->mutex_cond ); 

   } 

   return length; 

  } 

  从消息队列中接收消息: 

  消息队列的接收函数msqueue_receive,其参数:id为消息队列数组的索引号,buff为消息内容,length为消息长度。 

  int msqueue_receive ( id, buff, length ) 

  int id, length; 

  caddr_t buff; 

  { 

   caddr_t temp; 

   int pos; 

   if(id<0||id>=MAXQUEUE)return MQERROR; 

   if(length != msqueue[id]->elementLength) 

  return MQERROR; 

   //如果消息队列为空,则等待,直到消息队列为非空条件满足 

   if ( msqueue[id]->curElementNum == 0){ 

   pthread_mutex_lock ( &&msqueue[id]->mutex_cond ); 

   pthread_cond_wait ( &&msqueue[id]->cond, &&msqueue[id]->mutex_cond ); 

   pthread_mutex_unlock ( &&msqueue[id]->mutex_cond ); 

   } 

   //取消息前,锁住消息队列缓冲区,以免其他线程存放或取消息 

   pthread_mutex_lock ( &&msqueue[id]->mutex_buff ); 

   //为符合消息队列FIFO特性,取出消息后,进行消息队列的调整 

  temp = 

  malloc((msqueue[id]->curElementNum-1) 

  msqueue[id]-elementLength ); 

   bcopy ( &&msqueue[id]->buff[0], buff, msqueue[id]->elementLength ); 

   msqueue[id]->curElementNum --; 

   bcopy ( &&msqueue[id]->buff[msqueue[id]->elementLength], temp, 

   msqueue[id]->elementLength 

  msqueue[id]->curElementNum); 

   bcopy ( temp, &&msqueue[id]->buff[0], 

   msqueue[id]->elementLength 

  msqueue[id]->curElementNum); 

   free ( temp ); 

   //解除缓冲区锁 

   pthread_mutex_unlock ( &&msqueue[id]->mutex_buff ); 

   return length; 

  } 

  多任务模型的实现 

  在讨论完消息队列的创建、删除、发送和接收后,下面讲述消息队列在线程中的应用以实现多任务线程间的数据共享。 

  首先在main主函数中创建消息队列和线程: 

  //定义全局变量 

  Int msqueue_record, msqueue_process; 

  Void main() 

  { 

   pthread_t pthreadID1; 

   //创建消息队列,用于线程间通信 

   msqueue_record = msqueue_create (“record”, 200, 200); 

   msqueue_process = msqueue_create (“process”, 200, 200); 

   //创建数据采集线程 

   pthread_create ( &&pthreadID1, NULL, receiveData, NULL); 

   //创建数据处理线程 

   pthread_create ( &&pthreadID2, NULL, process, NULL); 

   //创建数据记录线程 

   pthread_create ( &&pthreadID1, NULL, record, NULL); 

   //等待进程结束 

   wait_thread_end( ); 

  } 

  数据采集线程: 

  voidreceiveData( ) 

  { 

   int count; 

   unsigned char buff[200]; 

   for(;;) { 

  //从数据口采集数据,并将数据放置于buff中 

  //wait_data_from_data_port( buff ) 

  //将数据写入消息队列msqueue_record中 

  msqueue_send ( msqueue_record, buff, 200 ); 

  //将数据写入消息队列msqueue_process中 

  msqueue_send ( msqueue_process, buff, 200 ); 

   } 

  } 

  记录线程函数: 

  voidrecord ( ) 

  { 

   int num, count; 

  unsigned char buffer[200]; 

   for ( ;; ) { 

  count = msqueue_receive ( msg_record, &&buffer, 200 ); 

  if ( count < 0) { 

  perror ( "msgrcv in record"); 

  continue; 

  } 

   //将取到的消息进行记录处理 

   //record_message_to_lib(); 

   } 

  } 

  数据处理线程函数: 

  int process( ) 

  { 

  int count; 

   unsigned char buffer[200]; 

   for ( ;; ) { 

   count = msqueue_receive ( msg_process, &&buffer, 200 ); 

  if ( count < 0) { 

   perror ( "msgrcv in record"); 

   continue; 

   } 

   //将取到的消息进行处理 

   //process_message_data() 

   } 

  } 

  在实现多任务系统时,作者曾经做过以下三种实现方法的比较:进程间通信采用IPC机制,线程间通信采用进程通信方式IPC,线程间通信采用基于作者开发的消息队列。结果表明:利用用户下的数据区进行线程间通信的速度最快,效率最高,而IPC方式慢。 


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangmeiqingtian/article/details/11474703

智能推荐

深度学习:可视化方法(模型可视化,训练过程可视化,特征提取可视化)_深度学习模型效果进行可视化-程序员宅基地

文章浏览阅读6.4k次,点赞8次,收藏67次。深度学习可视化,pytorch(tensorboard/netron)_深度学习模型效果进行可视化

操作系统常用词典(一)_min操作系统词汇大全-程序员宅基地

文章浏览阅读507次。操作系统常用词典(一) 操作系统(Operating System,OS):是管理计算机硬件与软件资源的系统软件,同时也是计算机系统的内核与基石。操作系统需要处理管理与配置内存、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。 shell:它是一个程序,可从键盘获取命令并将其提供给操作系统以执行。在过去,它是类似 Unix 的系统上唯一可用的用户界面。如今,除了命令行界面(CLI)外,我们还具有图形用户界面(GUI)。.._min操作系统词汇大全

时间序列--检验是否平稳_时间序列算法要求数据服从正态分布么-程序员宅基地

文章浏览阅读6k次。第一种方法:利用统计量You can split your time series into two (or more) partitions and compare the mean and variance of each group. If they differ and the difference is statistically significant, the time seri..._时间序列算法要求数据服从正态分布么

Jboss3.0-tomcat4.03的配置-4(数据库的配置) -程序员宅基地

文章浏览阅读231次。google_ad_client = "pub-8800625213955058";/* 336x280, 创建于 07-11-21 */google_ad_slot = "0989131976";google_ad_width = 336;google_ad_height = 280;//

erdas正射校正、数据融合、影像镶嵌_erdas imagine制作dtm和dom-程序员宅基地

文章浏览阅读1.3w次,点赞15次,收藏107次。在几个传统影像处理软件中,erdas的处理速度往往是最快的(比起ENVI、argis),而且img格式稳定,不易变化,个人使用首推erdas,当然大规模生产的话还是任务订单式的GXL好,它在批处理的路上走得更远,以web方式提交任务,以集群方式处理数据,高并发的处理能力,估计很多传统做影像处理的人要失业,扯远了。下面是望神州公司的一个erdas操作教程,本人无意抄袭,只想给正在用ERDAS的人..._erdas imagine制作dtm和dom

C语言 数据类型 字符型_字符 值-程序员宅基地

文章浏览阅读497次。n==\12==\xa都为换行的意思 \t缩格制表符 \\就是\ \' \"由此可以看出,无论用十进制、八进制还是十六进制都可以用来表示字符。要注意的是,在输入时空格也算字符、双字节编码 L是双字节字符前缀。数字与数字字符互转:+ -‘0’大小写字母互转:+ -32。_字符 值

随便推点

MTK Android Driver:GPIO_android gpio 配置-程序员宅基地

文章浏览阅读7.3k次,点赞3次,收藏17次。1.各列参数说明:1.1.Def.Mode:表示开机后gpio模块初始化后的模式,每个GPIOpin最多有7种模式,MODE0总是为GPIO mode。GPIO在系统初始化后的mode是由Def.Mode这一栏决定的。1.2.M0--M7:M0-M1-M3-M4-M5-M6-M7各个GPIO的可选的模式。如果勾选了M0-M7中的某些项,则会生成类似 #..._android gpio 配置

使用 python3.6 调用 jieba 库出现的 ValueError: dictionary file dict.txt must be utf-8 错误_dictionary file add_word_list.txt must be utf-8-程序员宅基地

文章浏览阅读6.6k次,点赞10次,收藏6次。错误类型: ValueError: dictionary file dict.txt must be utf-8解决方案:txt文件“另存为”设置编码格式为“utf-8”_dictionary file add_word_list.txt must be utf-8

JSONObect将map转化为java对象_jsonobjbect map转对象-程序员宅基地

文章浏览阅读184次。Oauth2AccessToken token = new JSONObject((Map) obj).toJavaObject(Oauth2AccessToken.class);_jsonobjbect map转对象

Unsupported compiler 'com.apple.compilers.llvmgcc42' selected for architecture 'armv7'-程序员宅基地

文章浏览阅读601次。Xcode8 报错:Unsupported compiler 'com.apple.compilers.llvmgcc42' selected for architecture 'armv7'解决方法:Change your compiler for C/C++/ObjectiveC Go to--->Build Settings--->Build OPtions-

python入门(13)异常与文件_except filenotfounderror:-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏3次。在Python中,我们可以自定义异常类来满足特定的异常情况。自定义异常可以继承自内置的异常类或其他已定义的异常类,以便更好地区分和处理特定类型的错误。if b == 0:try:main()在上述示例中,我们定义了一个名为的自定义异常类。它继承自内置的Exception类,并添加了__init__和__str__方法用于初始化异常对象和返回异常信息。在函数中,我们进行了除法运算,如果除数为零,则抛出异常,并传入错误信息。在main()函数中,我们捕获并处理异常,并打印异常信息。_except filenotfounderror:

Android面试攻略_详细了解在当今的社会里android工程师应具备什么的技能?并能详细说说自己的见解。-程序员宅基地

文章浏览阅读1.6k次。文章背景在外界看来IT是一个”高薪”的行业,都羡慕不已,熟不知IT也是一个苦逼的行业,每天加班加点,时刻用生命在写代码啊,导致许多程序猿和程序媛们都找不到对象,好不容易有个家庭的也影响夫妻生活、家庭生活哪,那么作为攻城狮为之能弥补方法之一无非就是不断提升自己,不断加薪,那么问题就来了,天下老板都爱画饼、只见其说,不见其果,那么攻城狮们跳槽成了加薪比较好的手段之一,导致IT部门人员更替频繁、_详细了解在当今的社会里android工程师应具备什么的技能?并能详细说说自己的见解。

推荐文章

热门文章

相关标签