linux下多线程程序设计,Linux下多线程编程详解(三)

【原创】手把手教你Linux下的多线程设计(三)--Linux下多线程编程详解

原创作者:Frozen_socker(冰棍)

E_mail:

线程互斥

互斥操作,就是对某段代码或某个变量修改的时候只能有一个线程在执行这段代码,其他线程不能同时进入这段代码或同时修改该变量。这个代码或变量称为临界资源。

例如:有两个线程A和B,临界资源为X,首先线程A进入,将X置为加锁状态,在A将锁打开之前的这段时间里,如果此时恰巧线程B也欲获得X,但它发现X处于加锁状态,说明有其它线程正在执行互斥部分,于是,线程B将自身阻塞。。。线程A处理完毕,在退出前,将X解锁,并将其它线程唤醒,于是线程B开始对X进行加锁操作了。通过这种方式,实现了两个不同线程的交替操作。

记住:一个互斥体永远不可能同时属于两个线程。或者处于锁定状态;或者空闲中,不属于任何一个线程。

代码如下:

None.gif//example_3.cNone.gif#includeNone.gif#includeNone.gif

None.gifvoid*pthread_func_test(void*arg);

None.gif

None.gifpthread_mutex_t mu;

None.gif

None.gifintmain()

ExpandedBlockStart.gif

ContractedBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.pnginti;

6a9c071a08f1dae2d3e1c512000eef41.png pthread_t pt;

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_init(&mu,NULL);//声明mu使用默认属性,此行可以不写6a9c071a08f1dae2d3e1c512000eef41.pngpthread_create(&pt,NULL,pthread_func_test,NULL);

6a9c071a08f1dae2d3e1c512000eef41.pngfor(i=0; i<3; i++)

ExpandedSubBlockStart.gif

ContractedSubBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_lock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.png printf("主线程ID是:%lu",pthread_self());//pthread_self函数作用:获得当前线程的id6a9c071a08f1dae2d3e1c512000eef41.pngpthread_mutex_unlock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.png sleep(1);

ExpandedSubBlockEnd.gif }ExpandedBlockEnd.gif}None.gif

None.gifvoid*pthread_func_test(void*arg)

ExpandedBlockStart.gif

ContractedBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.pngintj;

6a9c071a08f1dae2d3e1c512000eef41.pngfor(j=0; j<3; j++)

ExpandedSubBlockStart.gif

ContractedSubBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_lock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.png printf("新线程ID是:%lu",pthread_self());

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_unlock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.png sleep(1);

ExpandedSubBlockEnd.gif }ExpandedBlockEnd.gif}

终端输出结果:

None.gif主线程ID是 :3086493376None.gif新线程ID是 :3086490512None.gif

None.gif主线程ID是 :3086493376None.gif新线程ID是 :3086490512None.gif

None.gif主线程ID是 :3086493376None.gif新线程ID是 :3086490512

注:在你机器上运行的结果很可能与这里显示的不一样。

pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,都处于加锁状态中,即同一时间只能被一个线程调用执行。当另一个线程执行到pthread_mutex_lock处时,如果该锁此时被其它线程使用,那么该线程被阻塞,即程序将等待到其它线程释放此互斥锁。

上述例子中,涉及到了几个函数:pthread_mutex_init/pthread_mutex_lock/pthread_mutex_unlock/pthread_mutex_destroy/pthread_self

函数原型:

None.gifintpthread_mutex_init(pthread_mutex_t*restrict mutex,

None.gifconstpthread_mutexattr_t*restrict attr);

函数作用:

初始化互斥体类型变量mutex,变量的属性由attr进行指定。attr设为NULL,即采用默认属性,这种方式与pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER的方式等价。

函数原型:

None.gifintpthread_mutex_lock(pthread_mutex_t*mutex);

函数作用:

用来锁住互斥体变量。如果参数mutex所指的互斥体已经被锁住了,那么发出调用的线程将被阻塞直到其他线程对mutex解锁为止。

函数原型:

None.gifintpthread_mutex_unlock(pthread_mutex_t*mutex);

函数作用:

如果当前的线程拥有参数mutex所指定的互斥体,那么该函数调用将该互斥体解锁。

函数原型:

None.gifintpthread_mutex_destroy(pthread_mutex_t*mutex);

函数作用:

用来释放互斥体所占用的资源。

函数原型:

None.gifpthread_t pthread_self(void);

函数作用:获得线程自身的ID。前面我们已经提到过,pthread_t的类型为unsigned long int,所以在打印的时候要使用%lu方式,否则将产生奇怪的结果。

但是上面的代码并不完善,假设将循环次数修改得足够的长,打印后的结果可能并不是我们所希望看到的交替打印,可能象下面这样:

None.gif主线程ID是 :3086493376None.gif新线程ID是 :3086490512None.gif

None.gif主线程ID是 :3086493376None.gif新线程ID是 :3086490512None.gif

None.gif新线程ID是 :3086490512None.gif主线程ID是 :3086493376

这是什么原因呢?因为Linux是分时操作系统,采用的是时间片轮转的方式,主线程和新线程可能因为其它因素的干扰,获得了非顺序的时间片。如果想要严格的做到“交替”方式,可以略施小计,即加入一个标志。

完整程序如下:

None.gif//example_4.cNone.gif#includeNone.gif#includeNone.gif

None.gifvoid*pthread_func_test(void*arg);

None.gif

None.gifpthread_mutex_t mu;

None.gifintflag=0;

None.gif

None.gifintmain()

ExpandedBlockStart.gif

ContractedBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.pnginti;

6a9c071a08f1dae2d3e1c512000eef41.png pthread_t pt;

6a9c071a08f1dae2d3e1c512000eef41.png

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_init(&mu,NULL);

6a9c071a08f1dae2d3e1c512000eef41.png pthread_create(&pt,NULL,pthread_func_test,NULL);

6a9c071a08f1dae2d3e1c512000eef41.pngfor(i=0; i<3; i++)

ExpandedSubBlockStart.gif

ContractedSubBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_lock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.pngif(flag==0)

6a9c071a08f1dae2d3e1c512000eef41.png printf("主线程ID是:%lu",pthread_self());

6a9c071a08f1dae2d3e1c512000eef41.png flag=1;

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_unlock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.png sleep(1);

ExpandedSubBlockEnd.gif }6a9c071a08f1dae2d3e1c512000eef41.png pthread_join(pt, NULL);

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_destroy(&mu);

ExpandedBlockEnd.gif}None.gif

None.gifvoid*pthread_func_test(void*arg)

ExpandedBlockStart.gif

ContractedBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.pngintj;

6a9c071a08f1dae2d3e1c512000eef41.pngfor(j=0; j<3; j++)

ExpandedSubBlockStart.gif

ContractedSubBlock.gif...{

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_lock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.pngif(flag==1)

6a9c071a08f1dae2d3e1c512000eef41.png printf("新线程ID是:%lu",pthread_self());

6a9c071a08f1dae2d3e1c512000eef41.png flag==0;

6a9c071a08f1dae2d3e1c512000eef41.png pthread_mutex_unlock(&mu);

6a9c071a08f1dae2d3e1c512000eef41.png sleep(1);

ExpandedSubBlockEnd.gif }ExpandedBlockEnd.gif}

在使用互斥锁的过程中很有可能会出现死锁:即两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁,例如两个线程都需要锁定互斥锁1和互斥锁2,A线程先锁定互斥锁1,B线程先锁定互斥锁2,这时就出现了死锁。此时我们可以使用函数pthread_mutex_trylock,该函数企图锁住一个互斥体,但不阻塞。

函数原型:

None.gifintpthread_mutex_trylock(pthread_mutex_t*mutex);

函数pthread_mutex_trylock()用来锁住参数mutex所指定的互斥体。如果参数mutex所指的互斥体已经被上锁,该调用不会阻塞等待互斥体的解锁,而会返回一个错误代码。通过对返回代码的判断,程序员就可以针对死锁做出相应的处理。所以在对多个互斥体编程中,尤其要注意这一点。

经过以上的讲解,我们就学习了Linux下关于多线程方面对互斥体变量的操作。下一节,将给大家讲解有关线程同步方面的知识点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值