线程互斥
点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
1.线程互斥
到目前为止我们学了线程概念,线程控制接下来我们进行下一个话题,线程互斥。
有没有考虑过这样的一个问题,既然线程一旦被创建,几乎所有资源都是被所有线程共享的。 那多个线程访问同一份共享资源有没有什么问题?
下面我们模拟一下抢票的场景,看到底有没有什么问题
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <string>
//共享资源,火车票
int ticket = 10000;
void *GetTicket(void *args)
{
string name = static_cast<const char *>(args);
while (true)
{
if (ticket > 0)
{
cout << name << " 正在进行抢票: " << ticket << endl;
ticket--;
//以微秒为单位进行休眠,模拟真实的抢票要花费的时间
usleep(1000);
}
else
{
break;
}
}
}
int main()
{
pthread_t t1,t2,t3,t4;
pthread_create(&t1,nullptr,GetTicket,(void*)"thread->1");
pthread_create(&t2,nullptr,GetTicket,(void*)"thread->2");
pthread_create(&t3,nullptr,GetTicket,(void*)"thread->3");
pthread_create(&t4,nullptr,GetTicket,(void*)"thread->4");
pthread_join(t1,nullptr);
pthread_join(t2,nullptr);
pthread_join(t3,nullptr);
pthread_join(t4,nullptr);
return 0;
}
这好像没出问题啊,到0就停止了。
我们想看到的现象是抢到负数票,那怎么实现呢?
既然想看到抢到负数票,就需要尽可能的让多个线程交叉执行。
多个线程交叉执行的本质:就是让调度器尽可能的频繁发生线程调度与切换。
线程一般在时候发生切换呢?
时间片到了,来了更高优先级的线程,线程等待的时候。
那线程是什么时候检测上面的问题呢?
从内核态返回用户态的时候,线程要对调度状态进行检测,如果可以,就直接发生线程切换。
那修改一下代码
void *GetTicket(void *args)
{
string name = static_cast<const char *>(args);
while (true)
{
if (ticket > 0)
{
//线程进来之后先休眠,要被切走
usleep(1000);
cout << name << " 正在进行抢票: " << ticket << endl;
ticket--;
//以微秒为单位进行休眠,模拟真实的抢票要花费的时间
//usleep(1000);
}
else
{
break;
}
}
}
出问题了,放了10000张票,结果抢到了10002张票。现象就是这个样子。那为什么会出现这样的问题?
所谓判断的本质逻辑:
1.读取内存数据到CPU内部寄存器中
2.进行判断
所以ticket=1,多个线程可以同时执行这个判断语句。对不对?
答案是不对的。
我们只有一个CPU,只有一份寄存器,不能同时判断,但是注意我们写了usleep语句,线程是要被切换走的,但是寄存器中的内容是属于这个线程的,因此1也要被切走
剩下的线程就可以开始竞争执行if语句判断,但很不幸最终都是进去后先休眠。
当线程1被唤醒恢复上下文,执行到ticket- -;
ticket- -有三个步骤:1.读取数据,2.更改数据,3.写回数据
此时线程1从内存中读取ticke还是1,最后写回内存ticket为0
线程2此时醒来恢复上下文,它不知道内存中ticket此时是0,往下执行到ticket- -,此时取到ticket是0,最后写回内存中ticket是-1
线程3也醒来,和上面一样,执行到ticket- -,从内存取到数据为-1,写回内存中是-2
就是因为我们判断和更新分开,而在中间发生了大量的线程切换,最终可能出现ticket本来就是1了,但是你却放了大量线程同时进来对ticket变量做减减操作,进而导致我们的数据出现了负