linux 多线程意义,Linux多线程全局变量互斥访问哪几种机制,不同,含义?

写在前面:

为什么写这篇文章,本人开始对这三个量的理解非常之艰辛,啃了很久的《现代操作系统》,也看了很多博客,最后略有体会。这篇文章,是基于那本砖头书以及一些博客,加个人总结理解,希望对各位小伙伴有所帮助。(主要是怕我后面自己忘记了*-*)

何为互斥访问?

简而言之,当两个进程对一块共享内存区进行访问的时候,在A进入内存区的时候,B是不可以也不允许进入的,因为这样会引起不必要的混乱。那么如何防止这样的“抢进内存区”现象发生,我们需要互斥。

机制1 互斥锁:

我们知道进程间通信一个简单的解决办法就是使用互斥锁,即存在一把锁,当进程A要进入临界区的时候先测试这把锁,若果锁为0表示可以进出,那么A进入临界区,并且把锁变成1,告知别的进程无法进入,得等待(忙等待)。当A结束时,则退出临界区,并且把锁置为0,表示可以进入,那么这个时候B就可以“安全”的进入临界区了。

忙等待:

何为忙等待,简单地说就是指两个进程共享区域进行操作的时候,当A进程进入临界区的时候,由于竞争的存在那么,我们不可以让B进程进入临界区,必须等到A退出才可以进入。但是B得知道A何时退出,所以最直白的方式就是不断地询问A是否离开临界区。

这样子B进程就是处于忙等待的状态,也称自旋锁,很显然这是非常占用CPU资源的,所以我们需要避免。那么很容易让我们想到的就是阻塞B,从而让它不再轮询,占用CPU资源,故将其挂起,等到A出来后,给个信号让B进入临界区即可。也就是下文要说的(sleep和wakeup两个系统调用)

机制2 信号量:

信号量本身并不是POSIX IPC的机制之一,而是用于进程间同步于互斥的工具,作用类似于互斥锁,但是要复杂得多。对于共享存储区而言,信号量与互斥锁都可以用来进行互斥与同步,当我们把信号量设置为1时,就可以把它当做一个事实上的互斥锁。

信号量是为了解决同步问题

互斥量是为了解决互斥问题

信号量是一个整数,为了方便,我们把信号量计作S,信号量有两个原语,即P操作和V操作,一定要记住P操作和V操作都是原子操作,到底如何实现P、V操作,这是操作系统需要考虑的,同时需要硬件/CPU指令集支持,下文会详细介绍有关内容

P操作:S减1;

若S减1后仍大于或等于零,则进程继续执行;

若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度

V操作:S加1;

若相加结果大于零,则进程继续执行;

若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度

有一点很重要,信号量的操作只应该由内核去进行

例子:当信号量最大值为1

如果有三个线程/进程 A、B、C,都想要执行P操作,又因为P操作是原子的,那么ABC都执行了P(且没有进程执行了V操作),那信号量S将会是-2,而且后执行P操作的两个现场全部都要被加入到sleep进程队列中去

当成功执行P操作而且没有被挂起的进程执行了V操作之后,S变成了-1,这时候V操作还需要去sleep队列中去唤醒某个进程,至于到底唤醒哪一个,这取决于操作系统进程的调度方式了

然后重复上面的第二步直到S等于1

仔细看下上面的步骤,这不就是锁吗?

根据这个例子,你可能更容易理解这句话:

S大于等于零时代表可供并发进程使用的资源实体数,当S小于零时则表示正在等待使用临界区的进程数。

这个例子其实也就是很多人所说的,当信号量最大值为1(或者说不需要信号量的计数能力时),这种简化了功能的信号量就被称为互斥量。因为没有了技术能力,互斥量只有两种状态0/1。需要注意的是,这种信号量只是互斥量的一种实现方式,在概念上来讲,二者并没有直接的关系(或许从一开始互斥量是从信号量演化而来的,但是后来互斥量被单独拉成一个概念),互斥量有很多种实现方式(因为互斥量很简单)。

机制3 条件变量

其实没有概念,条件变量就是linux提供的一个编程接口,又有一些人喜欢把条件变量叫做条件锁,其实说的都是一种东西。条件变量其实就是解决了一个问题:一个进程等待某个变量成立,另外一个线程对这个变量进行修改,而这个问题必须避免发生竞态,所以往往必须要使用一个mutex,从而使得等待和修改都在同一个互斥量的临界区里。这样说可能很多人理解还是有偏差的,我们直接列出来Linux提供的接口,我们只看最关键的两个接口函数

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex); 使当前进程休眠,并且会释放这个mutex,并且在被唤醒时重新获取mutex

int pthread_cond_signal(pthread_cond_t *cond); 唤醒等待对应条件变量的进程

条件变量经常和互斥量在一起使用,这种模式会让一个线程锁住一个互斥量,然后当它不能获得它期待的结果是等待一个条件变量。最后由另一个线程向它发送信号,是它可以继续运行。

给个消费者-生产者的代码吧,基于条件变量和互斥锁

//此处使用的是pthread的条件变量与互斥量,完美实现生产者-消费者的问题

#include "pthread.h"

#include "stdio.h"

#include "stdlib.h"

#include "unistd.h"

#define NLOOP 20

#define BUFFSIZE 10//定义缓冲区为大小20

struct buf_t

{

int b_buf[BUFFSIZE];

int b_nitems;

int b_nextget;

int b_nextput;

pthread_mutex_t b_mutex; //互斥量

pthread_cond_t b_cond_consumer;//消费者的条件变量

pthread_cond_t b_cond_producer;//生产者的条件变量

}buf_t;

void *produce_loop(void *);

void *consume_loop(void *);

int main(int argc, char **argv)

{

intn;

pthread_ttidA, tidB;

printf("main, addr(n_in_stack) = 0x%x, addr(buf_t) = 0x%x, addr(produce_loop) = 0x%x\n", &n, &buf_t, &produce_loop);

pthread_create(&tidA, NULL, &produce_loop, NULL);

pthread_create(&tidB, NULL, &consume_loop, NULL);

/* wait for both threads to terminate */

pthread_join(tidA, NULL);

pthread_join(tidB, NULL);

exit(0);

}

void produce(struct buf_t *bptr, int val) /* 定义生产者的线程函数 */

{

pthread_mutex_lock(&bptr->b_mutex); /* down锁住缓冲区,互斥量mutex */

/* wait if buffer is fu11 */

while(bptr->b_nitems >= BUFFSIZE)

pthread_cond_wait(&bptr->b_cond_producer, &bptr->b_mutex); /* 由于缓冲区满了,等待条件变量的到来 */

printf ("produce %d\n", val);

bptr->b_buf[bptr->b_nextput] = val;

if (++bptr->b_nextput >= BUFFSIZE)

bptr->b_nextput = 0;

bptr->b_nitems++;

/*Signal consumer*/

pthread_cond_signal(&bptr->b_cond_consumer); /* 缓冲区满,发送信号给消费者,告知他来缓冲区取数据 */

pthread_mutex_unlock(&bptr->b_mutex); /* up释放缓冲区,互斥量mutex */

}

int consume(struct buf_t *bptr) /* 定义消费者的线程函数 */

{

int val;

pthread_mutex_lock(&bptr->b_mutex); /* down锁住缓冲区,互斥量mutex */

while (bptr->b_nitems <= 0)

pthread_cond_wait(&bptr->b_cond_consumer, &bptr->b_mutex); /* 由于缓冲区空了,等待条件变量的到来 */

val = bptr->b_buf[bptr->b_nextget];

printf("consume %d\n", val);

if (++bptr->b_nextget >= BUFFSIZE)

bptr->b_nextget = 0;

bptr->b_nitems--;

pthread_cond_signal(&bptr->b_cond_producer); /* 缓冲区空,发送信号给生产者,告知他来缓冲区生产数据 */

pthread_mutex_unlock(&bptr->b_mutex); /* up释放缓冲区,互斥量mutex */

return(val);

}

void * produce_loop(void *vptr)

{

int i;

printf("produce_loop thread, addr(stack) = %x\n", &i);

for(i = 0; i < NLOOP; i++){

produce(&buf_t, i);

}

return (NULL);

}

void * consume_loop(void *vptr)

{

int i, val;

printf("consume_loop thread, addr(stack) = %x\n", &i);

for(i = 0; i < NLOOP; i++){

val = consume(&buf_t);

}

return (NULL);

}

//非本人原创,本人只是加入注释和转载,这得谢谢我的指导老师

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值