判断sem信号量为零_信号量,锁和 golang 相关源码分析

原标题:信号量,锁和 golang 相关源码分析本文为社区粉丝原创投稿,再次感谢作者南瓜waniu的分享,欢迎大家在评论区留言和作者讨论,同时也欢迎大家踊跃投稿,分享您的golang语言学习经验!投稿邮箱地址为tougao@golang.ltd原创投稿:南瓜waniu1资源同步1.1 解决方案2 信号量2.1 共享变量2.2 信号量3 锁3.1 死锁3.2 活锁3.3 饥饿4 Golang syn...
摘要由CSDN通过智能技术生成

原标题:信号量,锁和 golang 相关源码分析

本文为社区粉丝原创投稿,再次感谢作者南瓜waniu的分享,欢迎大家在评论区留言和作者讨论,同时也欢迎大家踊跃投稿,分享您的golang语言学习经验!投稿邮箱地址为tougao@golang.ltd

原创投稿:南瓜waniu

1资源同步

1.1 解决方案

2 信号量

2.1 共享变量

2.2 信号量

3 锁

3.1 死锁

3.2 活锁

3.3 饥饿

4 Golang sync 包

4.4.1 数据结构

4.4.2 NewCond函数

4.4.3 Wait方法

4.4.4 Singal方法

4.4.5 Broadcast方法

4.3.1 数据结构

4.3.2 Add和Done方法

4.3.3 Wait方法

4.2.1 常量和结构

4.2.1 RLock和RUnlock方法

4.2.2 Lock和Unlock方法

4.1.1 接口和结构

4.1.2 Lock 方法

4.1.3 Unlock方法

4.1 sync.mutex.go

4.2 sync.rwmutex.go

4.3 sync.waitgroup.go

4.4 sync.cond.go1资源同步

并发已经成为现代程序设计中的重要考虑内容,但是并发涉及到一个很重要的内容就是资源同步。当两个或者多个线程访问同样的资源的时候,运行的结果取决于线程运行时精确的时序。这样导致结果与期望的结果大相径庭,因此我们需要对资源的访问顺序进行控制,已达到资源同步的目的。

1.1 解决方案

将共享资源(也就是共享内存)的程序片段成为临界区域,通过适当安排,使得两个者线程同时位于临界区域。对于临界区域访问解决方案,需要满足如下4个条件

任何两个线程不能同时位于临界区

不对CPU执行速度和时间做任何假设

临界区外运行的线程不阻塞其他线程

不能使线程无限期等待进入临界区

常见的互斥解决方案:

屏蔽中断线程的切换是由CPU中断机制提供的,如果一个线程进入临界区域后,CPU关闭中断响应;在离开临界区域后,再打开中断机制。那么在临界区域将不会有其他线程来竞争资源。 当时将屏蔽中断权利交给用户空间执行是不明智的,而且对于多核CPU而言没有效果。

锁变量几乎每一个编程语言都提供了资源同步方式:锁机制。该机制通过对资源进行Lock和Unlock,以达到对关键资源有序访问。

严格轮换法线程不停的执行CPU时间,连续测试某一个值是否出现。但是如果认为等待的时间非常短,可以使用该方式浪费CPU时间,用于等待的锁也成为自旋锁。

2 信号量 2.1 共享变量

在理解信号量之前,先了解采用共享变量使用多线程会出现什么问题。下面是一个C代码片段

1for(i=0; i

2cnt ++;

3}

cnt为全局变量,一个线程执行该代码片段的时候的汇编代码如下:

1mov q (%rdi), %rcx

2testq %rcx, %rcx

3jle .L2

4movl $0, %eax

5.L3:

6movq cnt(%rip), %rdx

7addq %eax

8movq %eax, cnt(%rip)

9addq $1, %rax

10cmpq %rcx, %rax

11jne .L3

12.L2

2.2 信号量其中6-8行分别对应对应着加载cnt,更新cnt和存储cnt。将cnt变量从内存位置读出,加载到CPU寄存器中,在CPU运算器中加1,然后存储到cnt的内存位置。虽然代码中cnt++只有一行,但是转换为汇编代码的时候不只有一个操作,也就是说该语句不是原子操作。如果多个线程同时执行代码,按照之前的条件,不对CPU的执行顺序做任何假设,如果其中线程a在执行7行汇编代码,而线程b执行6行汇编代码,那么b将"看不到"线程a对全局变量cnt加1的操作,那么每次执行的结果cnt也不完全一致。

计算机领域先驱Dijkstra提出经典的解决上述问题的方法:信号量(semaphore)。它是一个非负整数的全局变量。而且该变量只能有两个特殊操作来处理: P和V。

P(s): 如果s非零,那么P将s减1,并且立即返回。如果s为零,那么就挂起这个线程,知道s为非零。

V(s): V操作将s加1。如果有任何线程阻塞在P操作等待s非零,那么V将重启其中线程中的一个。

Posix标准定义需要操作信号量的函数

1#include

2intsem_init(sem_t*sem, 0, unsignedintvalue);

3intsem_wait(sem_t*s); /*P(s)*/

4intsem_post(sem_t*s); /*P(s)*/

那么如何使用信号量是的2.1小节出现同步问题解决呢?首先定义全局信号量

1volatilelongcnt = 0; /* global variable */

2sem_tmutex; /*global semaphore*/

初始化信号量,在这里初始值为1

1sem_init(&mutex, 0, 1);

最后使用信号量操作函数将临界区域代码包含起来

1for(i =0; i

2sem_wait(&mutex);

3cnt++;

4sem_post(&mutex);

5}

3 锁 3.1 死锁

首先看一下死锁的规范定义:

如果一个线程(进程)集合中的每一个线程(进程)都在等待只能由该线程(进程)集合中的其他线程(进程)才能引发的事件,那么该线程(进程)集合是死锁的。

举一个例子,如果线程 a 和线程 b 同是执行,线程a获取了资源r1,等待获取资源r2;而线程b获取了资源r2,等待获取资源r1。那么线程a和线程b组成的集合是死锁的。

预防死锁

破坏占有等待条件 对于需要获取多个资源的线程,一次性获取全部资源,而不是依次获取各个资源。

破坏环路等待条件 死锁集合的线程按照等占有线程和等待线程可以组成有向环图。那么如果对所有资源进行排序,所有线程按照资源顺序获取资源。3.2 活锁

在某些情况下,当线程意识它不能获下一个资源的时候,它会“礼貌性”地释放已经获得的资源,然后等待1ms,在尝试一次

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值