互斥量和临界区

本文介绍了为何需要互斥量以解决多线程并发访问共享变量的问题,详细讲解了互斥量的初始化、销毁以及加锁解锁的接口。同时强调了合理设定临界区大小的重要性,指出临界区过大或过小都会影响多线程的效率和安全性。

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

为什么需要互斥量?

大部分情况下,线程使用的数据都是局部变量,变量的地址在线程栈空间内,这种情况下,变量属于单个线程,其他线程无法获取这种变量。

如果所有的变量都是如此,将会省去无数的麻烦。但实际的情况是,很多变量都是多个线程共享的,这样的变量称为共享变量。可以通过数据得到共享,完成多个线程之间的交互与通信。

但是多个线程并发的操作共享变量会带来问题。

++操作,并不是一个原子操作(atomic operation),而是对应了如下三条汇编指令。

·Load:将共享变量global_cnt从内存加载进寄存器,简称L。
·Update:更新寄存器里面的global_cnt值,执行加1操作,简称U。
·Store:将新的值,从寄存器写回到共享变量global_cnt的内存地址,简称为S。

多线程在不加锁的情况下访问共享资源会产生难以预料的结果

应该避免多个线程同时操作共享变量,对于共享变量的访问,包括读写,都必须被限制为每次只有一个线程来执行。
锁是一个普遍的需求,但是让用户从零开始实现一个正确的锁也并不容易。正是因为这种需求具有普遍性,所以Linux提供了互斥量。

互斥量的接口

1.互斥量的初始化

互斥量采用的是英文mutual exclusive的缩写,即mutex。

正确的使用互斥量来保护共享数据,首先要定义和初始化互斥量。POSIX提供了两种初始化互斥量的方法。

第一种方法是将PTHREAD_MUTEX_INITIALIZER赋值给定义的互斥量如下:

    #include <pthread.h>
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

如果互斥量是动态分配的,或者需要设定互斥量的属性,那么上面静态初始化的方法就不适用了,NPTL提供了另外的函数pthread_mutex_init()对互斥量进行动态的初始化:

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

第二个pthread_mutexattr_t指针的入参,是用来设定互斥量的属性的,传递NULL即可,表示使用互斥量的默认属性。

调用pthrerad_mutex_init()之后,互斥量处于没有加锁的状态。

2.互斥量的销毁

在确定不再需要互斥量的时候,就要销毁它。但是在销毁之前,有三点要注意:

  • 使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量无须销毁
  • 不要销毁一个已加锁的互斥量,或者是真正配合条件变量使用的互斥量
  • 已经销毁的互斥量,要确保后面不会有线程在尝试加锁

销毁互斥量的接口如下:

int pthread_mutex_destroy(pthread_mutex_t *mutex);

当互斥量处于已加锁的状态,或者正在和条件变量配合使用,调用pthread_mutex_destroy函数会返回EBUSY错误码。

3.互斥量的加锁和解锁

POSIX提供了如下的接口:

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

临界区的大小

现在我们已经意思到需要用锁来保护共享变量。不过还另有一个需要注意的事项,即合理地设定临界区的范围。

第一临界区的范围不能太小,如果太小,可能起不到保护的目的。考虑如下的场景,如果哈希表中不存在某元素,那么向哈希表中插入某元素,代码如下:

if(!htable_contain(hashtable,elem.key))
{
pthread_mutex_lock(&mutex);
htable_insert(hashtable,&elem);
pthread_mutex_lock(&mutex);
}

从表面上看,哈希表是互斥的,插入时有锁的保护,但是对于是否有相同的元素的判断却没有加锁(即临界区较小),不能够达到预期的目的。当然临界区也不能太大,临界区的代码不能并行,如果临界区太大就会无法发挥多处理器多线程的优势。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值