【Linux】线程安全,mutex锁的使用

1.什么是线程安全?

当多个线程访问某个方法时,不管这些线程如何交替的执行,如果每次运行的结果和单线程运行的结果都是一样的,那么就可以说线程安全。线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步和互斥,否则的话就可能影响线程安全。

2. 互斥锁实现线程安全

2.1什么是互斥锁?

互斥锁可以对临界区上锁,互斥锁内部有一个计数器,其实就是互斥量,计数器的值只能为0或者为1,当线程需要进入临界区前,先要查看互斥锁的状态,如果计数器当前值为0,表示当前临界区已经有线程访问,所以不能进入临界区。如果计数器当前值为1,表示当前线程可以获取到互斥锁,也就是意味着可以进入临界区,在进入临界区时需要将计数器的值置1,这样别的线程就不能进入临界区了。

2.2互斥量接口的使用

2.2.1 互斥锁初始化

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

参数:
mutex : 要初始化的互斥量的地址, 类型为pthread_mutex_t;
attr: 设置互斥量的属性,通常可采用默认属性,即可将 attr 设为 NULL。
可以使用宏 PTHREAD_MUTEX_INITIALIZER 静态初始化互斥锁,比如:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

返回值:
成功: 0 默认互斥锁是打开的,也就是计数器的值为1.
失败: 非零,错误码

2.2.2互斥量的销毁

int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数mutex是互斥量的地址, 成功返回0,失败返回错误码需要注意的是:
(1)使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量无须销毁。
(2)不要销毁一个已加锁的互斥量, 或者是真正配合条件变量使用的互斥量。当互斥量处于已加锁的状态, 或者正在和条件变量配合使用, 调用pthread_mutex_destroy函数会返回EBUSY错误码。

2.2.3互斥量加锁:

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);

(1)第一个接口是阻塞加锁接口。mutex为传入互斥锁变量的地址如果mutex当中的计数器为1,返回0,表示加锁成功,同时计数器当中的值会被更改为0.如果mutex当中的计数器为0,pthread_mutex_lock接口就阻塞了,pthread_mutex_lock接口没有返回了,阻塞在函数内部,直到加锁成功。
(2)第二个接口为非阻塞接口,mutex中计数器为1时,加锁成功,计数器置为0,然后返回0表示加锁成功,mutex中计数器为0时,加锁失败,但也会返回,返回EBUSY表示加锁失败,线程不能进入临界区,非阻塞接口一般都需要搭配循环来使用。

2.2.4互斥量解锁:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

解锁的时候,会将互斥锁当中计数器的值从0变为1,表示其它线程可以获取互斥量。

2.3互斥量操作的原子性:

(1)首先互斥量也是一个临界资源, 许多线程对他进行操作难道不会产生二义性吗?
(2)互斥锁本质是一个0、1计数器,假如定义一个变量a, 赋值0 或 1来模拟互斥锁,比如进入临界区前判断a是否为1,如果是1 a–;并进入临界区。如果是零阻塞。出临界区后将a++;这样可以实现线程对临界资源的互斥访问吗?

答:(1)虽然互斥量也是临界资源但是对他的访问不会造成线程不安全。因为互斥锁的操作是原子性的,原子性保证了对互斥锁的操作可以在一个时间片内完成,所以当一个线程对互斥锁操作时,别的线程一定已经完成了对互斥锁的操作了,所以不会造成二义性。
(2)用变量a模拟互斥锁是不可行的, 因为a作为临界资源,而a的操作不具有原子性,访问a时需要将a从内存读到寄存器,然后++或者–操作后要写回内存。这一来一回一个时间片可能就不够用了。所以线程不安全。
那么互斥锁的操作是如何保证原子性的?
互斥锁的本质是一个0/1计数器,pthread_mutex_lock();加锁时,寄存器当中值直接赋值为0,将寄存器当中的值和互斥锁当中的值进行交换,判断寄存器当中的值,得出加锁结果。如果互斥锁为1,交换后为0,为0则不变。这样做可以既判断又完成互斥锁的赋值,只用一次访问内存,所以保证了操作的原子性。

3.死锁以及死锁的避免

3.1死锁的概念:

死锁是指两个或两个以上的进程(线程)在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace) ) ,若无外力作用,这些进程(线程)都将无法向前推进。比如线程1已经成功拿到了互斥量1, 正在申请互斥量2, 而同时在另一个CPU上,线程2已经拿到了互斥量2, 正在申请互斥量1。 彼此占有对方正在申请的互斥量,结局就是谁也没办法拿到想要的互斥量, 于是死锁就发生了。

3.2形成死锁的四个必要条件:

1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求该资源,则请求者只能等待,直至占有该资源的进程用毕释放。
(2)请求和保持条件:指进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其它进程占有,此时请求进程阻塞,但又对自己获得的其它资源保持不放。
(3)不剥夺条件:指进程已获得资源,在使用完之前,不能被剥夺,只能在使用完时由自己释放
(4)环路等待条件:指在发生死锁时,必然存在一个进程—资源的环形链,即进程集合(P0,P1,P2,…,Pn)中的P0正在等待一个P1占用的资源;P1正在等待一个P2占用的资源,……,Pn正在等待已被P0占用的资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值