Linux:互斥
目录
一、互斥概念
二、互斥的使用函数
三、互斥的底层原理
一、互斥概念
互斥,全称是线程互斥,互斥是一套解决方案,用来保护临界资源。一般在多线程的代码中,要使用互斥这套解决方案来保护临界资源。
主要从代码的角度理解互斥,下面的代码背景:多线程代码,有4个线程在其函数中访问了临界资源,现在用互斥来保护临界资源。
二、互斥的使用函数
man pthread_mutex_init
pthread_mutex_t 等于定义一把互斥锁。定义互斥锁有两种情况,
- 如果打算将互斥锁定义成全局或者静态的,则执行这条代码即可。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//1.定义全局互斥锁
pthread_mutex_t g_mutex= PTHREAD_MUTEX_INITIALIZER;
void route(ThreadData* td)
{
while(1)
{
pthread_mutex_lock(&g_mutex);
if(td->_tick>0)
{
usleep(1000);
printf("%s running,can get only tick:%d\n",td->_name.c_str(),td->_tick);
td->_tick--;
pthread_mutex_unlock(&g_mutex);
td->_total++;
}
else{
pthread_mutex_unlock(&g_mutex);
break;
}
}
}
- 如果打算将互斥锁定义在局部,则需要使用相关接口初始化和销毁。
- 更优雅的做法,把上锁和解锁做封装,交给类来做。
//3.RAII风格的加锁、解锁
void route(ThreadData* td)
{
while(1)
{
//在循环内创建一个类 临时对象,出循环销毁,调用析构函数,同时解锁
Lock lock(&td->_mutex);
if(td->_tick>0)
{
usleep(1000);
printf("%s running,can get only tick:%d\n",td->_name.c_str(),td->_tick);
td->_tick--;
td->_total++;
}
else{
break;
}
}
}
三、互斥的底层原理
- 为什么互斥锁能解决共享资源被多个线程访问的问题。
1.多线程本质是在并行运行程序,临界资源即共享资源,临界区即访问临界资源的代码,而保护共享资源,其实就是想办法在把临界区的代码变成多线程在串行访问。
2.专业来说,就是要求,临界区的代码要具备原子性,即要么还没开始做,要么已经完成。
3.在cpu执行代码的角度来看,要求临界区代码转成汇编后,只需一个时钟周期即可完成。
4.显然,临界区的代码,不可能只是一句汇编。
5.因此,有了互斥锁的设计,只需保证申请锁的过程是原子性的即可,让其他线程卡在申请锁这个语句即可。
- 底层汇编
上锁的过程,在转成汇编语句后,就是上面这几句汇编语言,其中最重要的一句就是xchgb语句,这个语句就是原子的,交换寄存器和内存中的值,而内存中的这个值代表着锁,寄存器的值却是0。
这个过程其实是这样的:内存中定义锁后,线程1执行xchgb后,意味着线程1申请到了锁,也意味着线程1 把临界资源上了锁,此时如果发生线程切换,线程2执行xchgb后,因为内存中已经没有了锁这个资源,所以后面的条件语句线程2也就无法执行。
这就是互斥锁的最底层实现原理。