linux多线程的同步机制有几种方式,我们分别来讲
1、互斥量
保证临界区同时有且仅有一个线程在访问
互斥量函数 :
初始化信互斥量pthread_mutex_init
加锁 pthread_mutex_lock()
解锁 pthread_mutex_unlock
在加锁和解锁之间执行的代码为临界区资源,加锁和解锁必须成对出现。使用互斥量来同步进程,往往会造成cpu的浪费,比如考虑如下代码,当临街资源sum 》100时我们将其赋值为0
开启三个线程,thread1(加操作),thread2(减操作),thread3(赋值为0)
thread1
void func1()
{
pthread_mutex_lock()
sum++
pthread_mutex_unlock()
}
thread2
void func2()
{
pthread_mutex_lock()
--sum
pthread_mutex_unlock()
}
thread3
void func3()
{
pthread_mutex_lock()
if(sum>100)
{
sum=0
pthread_mutex_unlock()
}
else
{
pthread_mutex_unlock()
}
}
从代码中我们可以看出,当sum不满100的时候,我们要去查询,并且sleep,如果当且仅当刚查询完sum不满100,然后立即sum满100,此时我们的线程仍然不得不睡眠100s这就浪费了cpu,所以为了充分利用cpu接下来引入了条件变量
2、条件变量
条件变量和互斥锁要同时使用,因为条件变量也要被保护,条件变量不同于互斥量需要不断的去轮询,=或者浪费cpu资源,条件变量会在条件不满足时阻塞,条件满足时释放变量,通知被阻塞的线程,而不许要不断的去轮询条件,
条件变量函数:
初始化条件变量:pthread_cond_init
等待条件变量 pthread_cond_wait()/pthread_cond_timewait()
通知条件变量满足条件:pthread_cond_signal()/pthread_cond_broadcast()
假设同样上述问题:
thread1
void func1()
{
pthread_mutex_lock()
sum++
pthread_mutex_unlock()
if (sum>100)
pthread_cond_broadcast()
}
thread2
void func2()
{
pthread_mutex_lock()
--sum
pthread_mutex_unlock()
}
thread3
void func3()
{
pthread_mutex_lock()
while(sum<100)
{
pthread_cond_wait()
}
sum=0
pthread_mutex_unlock()
}
这段代码的cpu利用效率旧远比上述直接使用互斥量来的高,我们使用互斥量访问临街区实现sum++,如果sum不大于100. pthread_cond_wait()就会一直阻塞,并且释放锁,当条件满足时pthread_cond_broadcast()会使pthread_cond_wait()函数成功返回,返回的同时将互斥量加锁,然后访问临界资源sum使其为0.然后释放锁。为什么我们会使用一个while拍判断sum<100而不是一个if语句,因为函数pthread_cond_broadcast()通知函数pthread_cond_wait()满足条件这期间有时间差,如果在通知的后,但pthread_cond_wait()还没来得及返回时,sum执行了减操作(因为pthread_cond_wait()函数没返回之前,是释放了锁的,所以此时可能会被线程二申请到锁,执行了减操作)此时sum就不满足条件了,所以我们要在一个while中判断,而不是,一个if,一个if没办法,保证sum的值在此期间满足条件。
3、信号量
信号量和互斥量最大的不同就是同时可以允许临界资源被多少线程访问,二进制信号量就和互斥量一样,pv操作承兑出现,且操作的值初始化为1,p操作,执行减1操作,相当于,一个线程申请了锁,v操作执行了加1操作,标明此时资源可以被访问,如果值为负数,则标明当前有多个线程在等待,这些线程会被防止等待队列中,等待由资源时被唤醒
thread1
void func1()
{
sem_wait(s)
sum++
sem_post(s)
}
thread2
void func2()
{
sem_wait(s)
--sum
sem_post(s)
}
通过加减s的值来控制临界资源的访问,如果s值初始化为1则为二进制信号量,非1,则同时允许多个线程同时访问临界资源