Linux的多线程--同步与互斥

本文介绍了Linux环境下多线程同步与互斥的概念,通过实例展示了无锁情况下全局变量增加操作的冲突。互斥锁作为解决方法,保证了同一时刻只有一个线程访问共享资源。文章详细讲解了互斥锁的初始化、销毁及加锁解锁操作,并探讨了死锁问题,包括死锁的定义、常见原因、必要条件以及避免死锁的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程的同步与互斥

在上一篇博客中我曾经提到,在Linux下多线程是共享数据的,但是共享数据可能会发生访问的冲突。比如两个线程都要把某个全局变量增加1,这个操作一般需要三条指令完成:

①从内存读变量值到寄存器

②寄存器的值加1

③将寄存器的值写回内存

假设两个线程在多处理器平台下同时执行者三条指令,很有可能会导致变量只加了一次而不是两次。

我们测试一下:

创建两个线程,各自将count增加5000次,正常情况下count的值应该是10000,但是事实上却是每一次的运行结果都不一定一样,有时比5000多,有时可能都到6000。


运行后的结果是


由此可见,对于多线程的访问,这里冲突了。

互斥:

从某种意义上来说,互斥就是一份资源在同一时刻只能被一个线程访问。通俗点说就是一把钥匙对应这一个门,而一次只能有一个人拿着钥匙去开门,而在开完门后门内的资源就一直被占据,其余的人想要进门就必须得等待那个人出来,这就是所谓的互斥。

同步:

同不就是进程按照一定的顺序访问临界资源,同步强调的是协同。通俗点说就是门外的人按照一定的顺序来们进行访问。

事实上,对于多线程的程序来说,访问冲突是非常普遍的,解决的方法就是引入互斥锁(Mutex,Mutual Exclusive Lock),获得锁的线程可以完成“读——修改——写”的操作,然后释放锁给其他的线程。没有获得锁的线程就只能等待而不能访问共享数据,这样“读——修改——写”三步操作组成一个原子操作,要么都执行,要么都不执行,而不会执行到一半被打断。

互斥锁操作函数

mutex用phread_mutex_t类型的变量表示。

初始化和销毁


我们可以看到,初始化有两种方式:

①调用函数pthread_mutex_init。参数mutex为在外面定义的一个pthread_mutex_t数据类型的数据指 针,init函数调用完成后,互斥锁的值会放在mutex指向的内存单元。要用默认的属性初始化互斥量,就将参数2attr设为NULL 。

②直接定义一个互斥锁,用下面宏定义(PTHREAD_MUTEX_INITIALIZER)给它赋值。此方式相当于方式①的attr设为NULL。即默认的属性初始化互斥量。

加锁解锁


其中lock与trylock都是加锁,区别在于lock是以阻塞式等待,而trylock是以非阻塞式等待。当一个线程已经占用锁的资源时,其他线程就会因为得不到锁资源而被阻塞,这时就需要使用trylock进行加锁了,如果调用时互斥量处于未锁住状态,那么pthread_mutex_trylock将会锁住该信号量,不会出现阻塞直接返回0,否则就会trylock失败,返回EBUSY。

下面我们再演示一下之前的代码,这次加上锁之后看得到的结果是不是10000.


运行后得到的结果是:


可以看到结果是10000.说明锁的作用体现了。

补充:死锁

什么是死锁?

在多任务系统下,当一个或多个进程等待系统资源,而资源又被进程本身或其它进程占用时,就形成了死锁。还用上面的例子解释就是有人拿了钥匙,但是他忘了自己已经拿了钥匙,然后就一直与其他人一样等着。这时就会出现死锁。

常见的死锁出现的原因:

①同一线程申请两次同样的锁资源

②两个线程互相申请彼此占有的锁资源

死锁产生的必要条件:

①请求与保持

②互斥属性

③不可剥夺、抢占

剥夺:Linux进程线程是可剥夺的(当时间片结束,系统强行剥夺,运行其他程序)

抢占:当一个进程或线程比正在运行的进程或线程的优先级高,则可发生抢占,Linux支持

④环路等待

如何避免死锁:

破坏任意4个产生条件中的一个就可以破坏死锁。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值