并发编程(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题

    boost的mutex,condition_variable非常好用。但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装。因此通过对原生态的POSIX 的mutex,cond的生成者,消费者的实现,我们可以再次体会boost带给我们的便利。

1. 什么是互斥量

       互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程在次被阻塞,等待下次运行状态。

pthread_mutex_t 就是POSIX对于mutex的实现。

函数名参数说明
pthread_mutex_init

pthread_mutex_t * mutex,

constpthread_mutex_t *attr
初始化一个互斥量,静态方式可以直接使用PTHREAD_MUTEX_INITIALIZER进行赋值初始化
pthread_mutex_destroypthread_mutex_t *mutex释放对互斥变量分配的资源。注意pthread_mutex_init有可能malloc了资源
pthread_mutex_lockpthread_mutex_t *mutex如果互斥量已经上锁,调用线程阻塞直至互斥量解锁
pthread_mutex_trylockpthread_mutex_t *mutex加锁,如果失败不阻塞
pthread_mutex_unlockpthread_mutex_t *mutex解锁
使用init函数进行初始化:
#include <pthread.h>pthread_mutex_t foo_mutex;void foo()
{pthread_mutex_init(&foo_mutex, NULL);pthread_mutex_lock(&foo_mutex);/* Do work. */pthread_mutex_unlock(&foo_mutex);pthread_mutex_destroy(&foo_mutex);
}


当然该初始化

pthread_mutex_init(&foo_mutex, NULL);

只能foo_mutex使用前初始化一次,最后destroy。初始化已经初始化的mutex将导致undefined behavior。

另外一种用法:

pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;
void foo()
{pthread_mutex_lock(&foo_mutex);/* Do work. */pthread_mutex_unlock(&foo_mutex);
}

当然了,这两种用法都有问题:如果在lock住后unlock之前出现exception,那么这个锁永远也不能unlock。这种情况下需要guard这个资源。具体可参照boost::mutex::scoped_lock的实现,非常简单但是极大简化了mutex的安全使用。

2. 什么是条件变量

      与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止。通常条件变量和互斥锁同时使用。

      条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

     条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

      条件变量的初始化和mutex的初始化差不多,也是有两种方式:

          pthread_cond_tmy_condition=PTHREAD_COND_INITIALIZER;

          也可以利用函数pthread_cond_init动态初始化。

下面中各个函数的简介。

函数名参数说明
pthread_cond_initpthread_cond_t *cond,
const pthread_condattr_t *attr
初始化
pthread_cond_destroypthread_cond_t *cond回收
pthread_cond_waitpthread_cond_t *cond,
pthread_mutex_t *mutex
等待,无超时
pthread_cond_timedwaitpthread_cond_t *cond,pthread_mutex_t *mutex,
const struct timespec *abstime
等待,有超时
pthread_cond_signalpthread_cond_t *cond一个在相同条件变量上阻塞的线程将被解锁。如果同时有多个线程阻塞,则由调度策略确定接收通知的线程
pthread_cond_broadcastpthread_cond_t *cond将通知阻塞在这个条件变量上的所有线程。一旦被唤醒,线程仍然会要求互斥锁。

一个简单使用条件变量进行线程同步的小例子:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;void *thread1(void *);
void *thread2(void *);int i=1;
int main(void)
{pthread_t t_a;pthread_t t_b;pthread_create(&t_a,NULL,thread2,(void *)NULL);/*create thread t_a*/pthread_create(&t_b,NULL,thread1,(void *)NULL); /*create thread t_b*/pthread_join(t_b, NULL);/*wait for exit of t_b*/pthread_join(t_a, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);exit(0);
}void *thread1(void *junk)
{for(i=1;i<=9;i++){pthread_mutex_lock(&mutex);if(i%3==0)pthread_cond_signal(&cond);elseprintf("thread1 running, i = %d\n",i);pthread_mutex_unlock(&mutex);sleep(1);}
}void *thread2(void *junk)
{while(i<9){pthread_mutex_lock(&mutex);if(i%3!=0)pthread_cond_wait(&cond,&mutex);/*..*/printf("thread2 running, i = %d\n",i);pthread_mutex_unlock(&mutex);sleep(1);}
} 

输出:
thread1 running, i = 1
thread1 running, i = 2
thread2 running, i = 3
thread1 running, i = 4
thread1 running, i = 5
thread2 running, i = 6
thread1 running, i = 7
thread1 running, i = 8
thread2 running, i = 9

3. 生产者-消费者的实现

        生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。    

#include <stdio.h>
#include <stdlib.h>
#define MAX 5pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥锁*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;/*初始化条件变量*/typedef struct{char buffer[MAX];int how_many;
}BUFFER;BUFFER share={"",0};
char ch='A';/*初始化ch*/void *producer(void *);
void *consumer(void *);int main(void)
{pthread_t t_read;pthread_t t_write;pthread_create(&t_write,NULL,producer,(void *)NULL); /*创建进程t_a*/pthread_create(&t_read,NULL,consumer,(void *)NULL); /*创建进程t_b*/pthread_join(t_write,(void **)NULL);pthread_join(t_read, NULL);exit(0);
}void *producer(void *junk)
{int n=0;printf("Producer: starting\n");while(ch!='K'){pthread_mutex_lock(&mutex);/*锁住互斥量*/if(share.how_many!=MAX){share.buffer[share.how_many++]=ch++;/*把字母写入缓存*/printf("Producer: put char[%c]\n",ch-1);/*打印写入字母*/if(share.how_many==MAX){printf("Producer: signaling full\n");pthread_cond_signal(&cond);/*如果缓存中的字母到达了最大值就发送信号*/}}pthread_mutex_unlock(&mutex);/*解锁互斥量*/}sleep(1);printf("Producer:Exiting\n");return NULL;
}void *consumer(void *junk)
{int i;int n=0;printf("Consumer: starting\n");while(ch!='K'){pthread_mutex_lock(&mutex);/*锁住互斥量*/printf("\nConsumer : Waiting\n");while(share.how_many!=MAX)/*如果缓存区字母不等于最大值就等待*/pthread_cond_wait(&cond,&mutex);printf("Consumer: getting buffer:: ");for(i=0;share.buffer[i]&&share.how_many;++i,share.how_many--)putchar(share.buffer[i]); /*循环输出缓存区字母*/putchar('\n');pthread_mutex_unlock(&mutex);/*解锁互斥量*/}printf("Consumer: exiting\n");return NULL;
}
输出:

Producer: starting
Producer: put char[A]
Producer: put char[B]
Producer: put char[C]
Producer: put char[D]
Producer: put char[E]
Producer: signaling full
Consumer: starting

Consumer : Waiting
Consumer: getting buffer:: ABCDE

Consumer : Waiting
Producer: put char[F]
Producer: put char[G]
Producer: put char[H]
Producer: put char[I]
Producer: put char[J]
Producer: signaling full
Consumer: getting buffer:: FGHIJ
Consumer: exiting
Producer:Exiting

转载于:https://www.cnblogs.com/wuwa/p/6190832.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/300266.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

送给销售一族

投身销售英勇无畏工作行业看似高贵其实生活极其琐碎为了生计吃苦受累鞍前马后终日疲惫客人投诉照死赔罪点头哈腰就差下跪日不能息夜不能寐老板一叫立即到位一年到头加a班受罪劳动法规统统作废身心交瘁暗自流泪屁大点事反复开会逢年过节家人难会分分秒秒不敢离位迎接审核让人崩溃…

akka linux 端口,Actor模型开发库 Akka

Akka 是一个用 Scala 编写的库&#xff0c;用于简化编写容错的、高可伸缩性的 Java 和 Scala 的 Actor 模型应用。Actor模型并非什么新鲜事物&#xff0c;它由Carl Hewitt于上世纪70年代早期提出&#xff0c;目的是为了解决分布式编程中一系列的编程问题。其特点如下&#xff1…

简单粗暴的肢体语言解读攻略 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;

基于事件驱动架构构建微服务第1部分:应用程序特定的业务规则

原文链接&#xff1a;https://logcorner.com/building-microservices-through-event-driven-architecture-part1-application-specific-business-rules/如今&#xff0c;洋葱或六边形等架构为代码的可测试性和维护、与外部框架的独立性提供了重要帮助。在本教程中&#xff0c;我…

WinhexV13.2汉化版

WinhexV13.2sr-11_H.rar转载于:https://blog.51cto.com/www520/16874

JavaWeb 项目启动时,后台开启一个线程的方法

原文链接&#xff1a;http://blog.csdn.net/it_wangxiangpan/article/details/7168286JavaWeb 服务启动时&#xff0c;在后台启动加载一个线程。目前&#xff0c;我所掌握的一共有两种方法&#xff0c;第一种是监听&#xff08;Listener&#xff09;&#xff0c;第二种是配置随…

linux脚本 逻辑运算,Linux-shell-逻辑运算和;

7.Shell7.5.2命令执行的判断依据&#xff1a; ; , &&, ||1.cmd ; cmd (不考虑指令相关性的连续指令下达)一般用于多条命令之间没有直接需求联系&#xff0c;最多只是有一个执行先后的关系。[rootlocalhost tmp]# sync; shutdown -h now2.与&&或 ||指令下达情况…

我居然从一只猫身上学到了斐波那契数列

猫的数学这么好是有原因的斐波那契数列&#xff08;Fibonacci sequence&#xff09;是由数学家列昂纳多斐波那契定义的把它写成数列的形式是这样的&#xff1a;1,1,2,3,5,8,13,21,34,55,89,...比如&#xff1a;人的耳朵比如&#xff1a;台风比如&#xff1a;松果的底部螺纹从两…

听说过Netflix的Chaos Monkey吗?不用羡慕,我们.NET也有

Chaos Monkey&#xff0c;是Netflix工程师创建的一种故障注入系统&#xff0c;它会随机在生产实例中引发各种各样的故障或异常&#xff0c;以确保它们的系统能够在这样的情况下存活&#xff0c;而不会对客户造成任何影响。可见&#xff0c;Chaos Monkey可以提高系统的安全和可用…

(ZT)VC++的链接错误LNK2001

学习VC&#xff0b;&#xff0b;时经常会遇到链接错误LNK2001&#xff0c;该错误非常讨厌&#xff0c;因为对于编程者来说&#xff0c;最好改的错误莫过于编译错误&#xff0c;而一般说来发生连接错误时&#xff0c;编译都已通过。产生连接错误的原因非常多&#xff0c;尤其LNK…

PHP中session与cookie的简单使用

2019独角兽企业重金招聘Python工程师标准>>> cookie简单实例&#xff1a; <?php if($_GET[out]){ //注销cookie setcookie(id,); setcookie(password,); echo "<script>location.hrefcookie.php</script>"; …

linux下安装服务,linux下的软件服务安装管理

在centos7下&#xff0c;为系统装一个服务有两种方式&#xff0c;一种是通过系统提供的rpm/yum自动安装&#xff0c;一种是通过服务的官网提供的安装包进行安装。rpm/yum 服务的安装及管理yum -y install 包名: 可以通过yum方式默认安装当前yum源中提供的软件服务&#xff0c;y…

NetBeans Weekly News 刊号 # 27 - Sep 24, 2008

刊号 # 27 - Sep 24, 2008 日程表 注册 NetBeans Day--圣保罗&#xff0c;巴西&#xff08;十月一日&#xff09; 欢迎来到巴西圣保罗的 Sun Tech Days 。赶快在十月一日加入我们的 NetBeans Deep Dive 吧&#xff01;注册 NetBeans Day 是免费的&#xff0c;即使您不参加 Sun …

做项目开发你必须得掌握的知识:设计模式

先分享一个小故事 两个年轻人是大学同班同学&#xff0c;毕业后被同一家公司录取&#xff0c;可以说是站在相同的起跑线上。两人对未来也都是信心满满&#xff0c;踌躇满志。其中一人怀抱满腔激情&#xff0c;到处学习热门框架&#xff0c;但受限于公司体量和业务逻辑&#xff…

985硕博士:你为什么比我差?

全世界只有3.14 % 的人关注了青少年数学之旅身边总有些人看上去很轻松&#xff0c;不仅在工作中游刃有余&#xff0c;还知识渊博&#xff0c;对各种事情有自己的思考。这次&#xff0c;我们非常认真地筛选了这些公众号&#xff0c;他们专注于内容&#xff0c;关心当下发生的事情…

熊猫烧香是天才作品吗?

今天在新闻中看到有关武男的消息&#xff0c;很不幸&#xff0c;再次发现媒体称武男——李俊为天才。这年头&#xff0c;程序员队伍已经非常壮大了&#xff0c;称得上天才作品的有哪些呢&#xff1f;从我自己的角度列举一下&#xff0c;当然不止这些。字处理&#xff1a;WPS DO…

ESXI转HYPER-V,问题接二连三啊(VMDK转VHD)

首先说软件&#xff1a; 要不是用SCVMM来转的话&#xff0c;我用得最爽的还是WINIMAGE&#xff0c;自然流畅。其它的都有各种问题。 其次说说配置更改&#xff1a; 如果原ESXI里只有一个硬盘&#xff0c;一切好说&#xff0c;如果里面挂载了两个&#xff0c;甚至三个硬盘&#…

在linux中查找运行程序句柄,如何查找我的进程在Linux中打开的文件句柄?

首先&#xff0c;您并不需要关心很多关于您不知道的打开的文件描述符。如果你知道你不会再写信给他们&#xff0c;关闭它们是一个好主意&#xff0c;并且不会伤害 - 毕竟你只是做了一个fork()&#xff0c;这个fds打开了两次。但是同样&#xff0c;如果你让他们开放&#xff0c;…

ASP.NET2.0_多语言本地化应用程序

1.设置当前文化 页面类有两个影响本地化的重要属性均在< % Page % >指令设置:1).UICulture 此属性用于指定载入到页面的资源文件,资源文件可以包含页面文本内容的特定语言的翻译版本.其属性值可为中立文化也可为特定文化(下面有介绍&#xff09;2).Culture …

不用“背”单词,一个方法从普通二本到哥伦比亚大学:我是如何做到的?

全世界只有3.14 % 的人关注了青少年数学之旅“学英语太难、太费劲了。”我听过无数人这么说。然而&#xff0c;我的学员们&#xff0c;却都只用了短短3—6个月时间&#xff0c;就以惊人速度提高了英语&#xff1a;小磊&#xff1a;勉强踩着2本线上了大学&#xff0c;四级考了3次…