我们都知道每个线程都有自己独立的栈空间,自己使用的变量地址也在栈空间内。但是有时候,线程访问的变量都需要线程共享,这些变量称为共享变量,通过数据共享完成线程之间的交互。
我们在进程中了解到,多个进程同时访问一个共享变量的时候,会引发很多问题。所以进程之间需要访问共享变量的时候,需要互斥。
而对应的,我们线程访问共享变量,也需要这样的互斥操作。
我们从一个卖票系统看看,如果没有使用互斥相关操作,看看会出现什么样的结果。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int ticket = 4; //票的总数
void *rout(void *arg)
{
char *id = (char *)arg;
while(1)
{
if(ticket > 0)
{
usleep(1000);
printf("%s sell ticket:%d \n",id,ticket);
ticket--;
}
else
{
break;
}
}
}
int main()
{
pthread_t tid1,tid2,tid3;
pthread_create(&tid1,NULL,rout,"thread1");
pthread_create(&tid2,NULL,rout,"thread2");
pthread_create(&tid3,NULL,rout,"thread3");
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
return 0;
}
看一下运行结果:
很明显,运行的结果显示,不符合我们预期的效果。
我们的票数为4张,但是可以看到最后thread2 都卖到了 -1 张票了,这是不符合实际的问题的结果。
由于ticket 是全局变量,每个线程都可以访问到它,每个线程都在可能切换到运行状态,在线程函数里,出现了usleep() 等待,这时候系统可能切换到另一个进程中,又卖出了一张票,等该线程的运行时间到了后,再返回原来线程的时候,还继续对票数减一了。这就导致了程序的不符合实际性。
我们应该怎么解决这个问题呢?
1.代码必须要有互斥行为,当代码进入临界区执行时,不允许其他线程进入该临界区。
2.如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行时,那么只允许一个线程进入该临界区。
3.如果线程不在临界区中执行,那么该线程不能阻止其他线程进入到临界区。
这就是给临界区上了一把锁。 Linux上提供的这边锁叫做互斥量。
我们还可以这样定义互斥量:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER ;
这样定义的互斥量不需要进行初始化和删除,系统会自动的初始化和删除。
互斥量:
1.定义一个互斥量 pthread_mutex_t mutex
2.初始化信号量 pthread_mutex_init(&mutex,NULL); //初始化成1
3.上锁 pthread_mutex_lock(&mutex);
1->0,返回
0 ,等待
4.销毁 pthread_mutex_destroy(&mutex);
这样我们改进一下我们的买票系统:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
int ticket = 4;
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex; //定义互斥量
void *rout(void *arg)
{
char *id = (char *)arg;
while(1)
{
pthread_mutex_lock(&mutex); //上锁
if(ticket > 0)
{
usleep(1000);
printf("%s sell ticket:%d \n",id,ticket);
ticket--;
pthread_mutex_unlock(&mutex); //解锁
}
else
{
pthread_mutex_unlock(&mutex); //解锁
break;
}
}
}
int main()
{
pthread_t tid1,tid2,tid3;
pthread_mutex_init(&mutex,NULL); //初始化互斥量
pthread_create(&tid1,NULL,rout,"thread1");
pthread_create(&tid2,NULL,rout,"thread2");
pthread_create(&tid3,NULL,rout,"thread3");
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
pthread_join(tid1,NULL);
pthread_mutex_destroy(&mutex); //销毁互斥量
return 0;
}
运行结果图:
添加了互斥量过后,该系统的买票结果与我们的预想效果一样。