在linux多线程编程开发中最基本概念主要包含三点:线程,互斥锁,条件变量
线程:线程的创建,退出,等待 3 种。(这次主题不是这,就不细说了)
互斥锁:包括 4 种操作,分别是创建,销毁,加锁和解锁。
条件变量:有 5 种操作:创建,销毁,触发,广播和等待。
其中互斥锁和条件变量基本上是一起使用,其原因是互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。(拿最经典的生产者和消费者模型来说,当生存的速度跟不上消费的速度,就有大量的消费者线程在锁前阻塞),而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。
- 相应API
互斥锁API:
创建:对于静态分配的互斥量, 可以把它设置为PTHREAD_MUTEX_INITIALIZER, 或者调 用pthread_mutex_init
init原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric attr);
销毁:int pthread_mutex_destroy(pthread_mutex_t *mutex);
加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);
解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值: 成功则返回0, 出错则返回错误编号.
条件变量API:
创建:条件变量和互斥锁一样,都有静态动态两种创建方式,静态方式使用 PTHREAD_COND_INITIALIZER常量,如下:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER
动态方式调用cond初始化函数pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr)
销毁:pthread_cond_destroy(pthread_cond_t *cond)
触发:pthread_cond_signal (pthread_cond_t *cond);
广播:pthread_cond_broadcast (pthread_cond_t *cond);
等待:pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) - 相互联系
互斥锁的作用:互斥锁(也叫互斥量),它的作用是用来保护一段区域,使之成为一块临界区,让其进行原子操作。工作原理简单的来说就是当有线程来访问这一块临界区的时候,就会试图获得这把锁,如果这把锁被其他线程拿到的话,就会被阻塞,直到这把锁被释放。
(适用场景:如果买票系统最后只有一张票了,有n个线程同时买这张票,可能导致只有一张票,却有多个买到的客户)
条件变量的作用:在上面的场景中,票的供应不能满足客户的需求,就会使拿不到锁的线程一直在等待的队列中阻塞,导致资源浪费。(也就是买票人员一直在车站等待)。而条件变量是一种同步机制,让条件满足的时候(就是没票了,你可以先去做你自己的事情)允许线程挂起,直到共享数据上的某些条件得到满足(票制作完毕,通知你可以买票了)。然后它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程(通知可以买票了)。这些线程将重新锁定互斥锁并重新测试条件是否满足(最好用while再判断一次,防止假醒)。
3. 代码体现
经典的消费者生产者模型
让我们先用通俗的话来描述整个流程是:
消费者:
第一步:pthread_mutex_lock->对临界区进行加锁
第二步: while 没货物的时候:pthread_cond_wait 让消费者进行等待(这里其实3个动作的合集,1让这个线程把锁丢掉(解锁),2使线程休眠,等代pthread_cond_signal发送信号,3重新获得锁(加锁))
第三步:买东西
第四步:pthread_mutex_unlock->对临界区进行解锁
生产者:
第一步:pthread_mutex_lock->对临界区进行加锁(用的拿起消费者pthread_cond_wait丢掉的锁)
第二步:制作货物,完毕后pthread_cond_signal发送信号,自己上锁,告诉消费者可与获得锁了
第三步:pthread_mutex_unlock->对临界区进行解锁
代码片段
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//初始化互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//初始化条件变量
void *produce(void *arg);
void *consume(void *arg);
int goods=1;//货物
int main(void)
{
int i=0;
pthread_t t_a;//一个生产者
pthread_t t_b[4];//4个消费者
pthread_create(&t_a,NULL,produce,(void *)NULL);//创建进程t_a即生产者
for(i=0;i<4;i++)
{
pthread_create(&t_b[i],NULL,consume,(void *)&i); /*创建进程t_b[i],即4个消费者*/
}
pthread_join(t_a, NULL);//等待生产者结束
for(i=0;i<4;i++)
{
pthread_join(t_b[i], NULL);//等待消费者结束
}
pthread_mutex_destroy(&mutex);//销毁锁
pthread_cond_destroy(&cond);//销毁条件变量
exit(0);
}
void *produce(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);//锁住互斥量(拿到消费者pthread_cond_wait丢掉的锁)
if(goods==0)
goods=20;//生产货物
pthread_cond_signal(&cond);//条件改变,发送信号,通知消费者进程
pthread_mutex_unlock(&mutex);//解锁互斥量
sleep(1);
}
pthread_exit(0);
}
void *consume(void *arg)
{
int i=0;
i=*(int *)arg;
printf("comsume:%d/n",i);
while(1)
{
pthread_mutex_lock(&mutex);
while(goods==0)//没有货物了就wait
pthread_cond_wait(&cond,&mutex);//没有货物了,线程休眠,丢弃锁(好让生产者拿到锁),等待
goods--;//消费
pthread_mutex_unlock(&mutex);
sleep(1);
}
pthread_exit(0);
}