Linux互斥锁的理解和使用

互斥的理解

一句话理解互斥: 等我用完厕所,你再用厕所。
什么是互斥?你我早起都要用厕所,谁先抢到谁先用,中途不被打扰。
伪代码如下:
void 抢厕所(void)
{
if (有人在用) 我眯一会;
用厕所;
喂,醒醒,有人要用厕所吗;
}
假设有 A、 B 两人早起抢厕所, A 先行一步占用了; B 慢了一步,于是就眯一会;当 A 用完后叫醒 B, B也就愉快地上厕所了。
在这个过程中, A、 B 是互斥地访问“厕所”,“厕所”被称之为临界资源。我们使用了“休眠-唤醒”的同步机制实现了“临界资源”的“互斥访问”。

总结:互斥锁是为了防止竞争共享资源,只有在持有锁的线程将锁解锁释放后,其它线程才能进行抢锁加锁操作。

使用场景

情景一:两个线程都在使用同一个全局变量。
例如:执行下面代码,最后的number值会是多少?21或22?其实不然

int number = 10;
void p(void)
{
    number*=2;
    printf("%d\n",number);
}
void q(void)
{
    number+=1;
    printf("%d\n",number);
}
int main(void)
{
    pthread_t tid1,tid2;
    pthread_create(&tid1,NULL,(void*)(&p),NULL);
    pthread_create(&tid2,NULL,(void*)(&q),NULL);
    pthread_join(tid1);
    pthread_join(tid2);
}

分析:下图表示了两个线程对全局变量操作的一种情况,线程1和2都拿到number的初始值,线程1操作后将11放回变量空间,但是线程2不久后将20也放回变量空间将11覆盖
在这里插入图片描述

使用互斥锁,即可解决这个问题。

使用方法

头文件:#include <pthread.h>
锁类型结构:pthread_mutex_t
创建互斥锁:pthread_mutex_init()
加锁:pthread_mutex_lock()
解锁:pthread_mutex_unlock()
测试加锁:pthread_mutex_trylock();//与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。

加锁示例代码:

int number = 10;
pthread_mutex_t mutex; /*互斥量 */
void p(void)
{
	pthread_mutex_lock(&mutex); /*获取互斥锁*/
    number*=2;
	printf("%d\n",number);	
	pthread_mutex_unlock(&mutex); /*释放互斥锁*/
}
void q(void)
{
	pthread_mutex_lock(&mutex); /*获取互斥锁*/
    number+=1;
	printf("%d\n",number);
	pthread_mutex_unlock(&mutex); /*释放互斥锁*/
}
int main(void)
{
	pthread_t tid1,tid2;
	pthread_mutex_init (&mutex, NULL);
    pthread_create(&tid1,NULL,(void*)(&p),NULL);
    pthread_create(&tid2,NULL,(void*)(&q),NULL);
    pthread_join(tid1);
    pthread_join(tid2);
}

使用原则

  • 对共享资源操作前一定要获得锁。
  • 完成操作以后一定要释放锁。
  • 尽量短时间地占用锁。
  • 如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。
  • 线程错误返回时应该释放它所获得的锁。

互斥失败的例子

static int valid = 1;

static ssize_t gpio_key_drv_open (struct inode *node, struct file *file)
{
     if (!valid)
     {
             return -EBUSY;
     }
     else
     {
             valid = 0;
     }
     return 0; //成功
}

static int gpio_key_drv_close (struct inode *node, struct file *file)
{
     valid = 1;
     return 0;
}

上面示例中,通过变量valid来实现互斥,很大概率没有问题,但是并无万无一失。
分析
程序A调用驱动程序时,它可能被程序B打断,程序B也去调用这个驱动程序。下图是一个例子,程序A在调用驱动程序的中途被程序B抢占了CPU资源:
在这里插入图片描述

程序A执行到第11行之前,被程序B抢占了,这时valid尚未被改成0;程序B调用gpio_key_drv_open时,发现valid等于1,所以成功返回0;当程序A继续从第11行执行时,它最终也成功返回0;这样程序A、B都成功打开了驱动程序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值