C++实现多线程三窗口卖票程序

        假设我们有100张门票,有三个售卖窗口,我们希望以多线程的方式将这些票卖出去,这样效率会更高一些。

        首先我们需要有一个全局的门票变量。

int tickts = 100;

        卖票的函数,由于每个窗口卖出一张票都需要花费一些时间,假设是100ms,那么可以通过线程睡眠函数实现这一时间的花费。

void selltickts(int number)
{
	while (tickts > 0)
	{
		std::cout << tickts<<std::endl;
		tickts--;
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//卖一张票花费100ms
	}
}

main函数,创建窗口。

int main()
{
	std::vector < std::thread >v;
	for (int i = 1; i <= 3; i++)
	{
		v.push_back(std::thread(selltickts, i));
	}
	for (int i = 0; i < 3; i++)
	{
		v[i].join();
	}
	return 0;
}

运行结果如下:

         这个运行结果显然是出问题的,基本上每张票都重复售卖了3次。这主要是因为我们使用了tickts--;这一操作,--和++这两个运算符,本身就是线程不安全的,它们都是由三条汇编机器指令组成的。实际上,一个线程在执行一个函数时,不一定会将一个函数的一行代码都完整执行掉,比如tickts--就没有完整执行,但是它一定会将一行代码中的一行汇编指令完整执行掉,执行完了这条汇编指令之后,这个线程就有可能将自己的时间片归还给操作系统,自身陷入阻塞状态,等待下次获取时间片后进行执行剩下的汇编指令。这样就会导致多个线程同时操作一张门票,三个窗口同时在卖一张门票,这是不允许的,我们需要将这三个线程设置为互斥状态,这样它们就不能同时操作一个资源了。这时候就需要用到互斥锁了。

        我们添加一个全局的互斥锁。

std::mutex mtx;

        将售票函数用互斥锁进行锁定。 

void selltickts(int number)
{
	mtx.lock();
	while (tickts > 0)
	{
		std::cout << tickts<<std::endl;
		tickts--;
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//卖一张票花费100ms
	}
	mtx.unlock();
}

运行程序。

        这次结果虽然正确了,但是由于我们在while循环外面,进行互斥锁定,当有一个线程拿到这个锁时,就进入到了while循环中,其他线程拿不到这个锁,就进入不到while循环里面了,所以这个程序从始至终都只是一个单线程程序,与我们的预期不符合。 

        我们将锁定行为放入while循环中。

void selltickts(int number)
{
	while (tickts > 0)
	{
		mtx.lock();
		std::cout << tickts<<std::endl;
		tickts--;
		mtx.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//卖一张票花费100ms
	}
}

运行程序。

        显然程序还是不对,因为最后卖出了第0张票,这种情况可能是这样的,当票数还有1张时,假如一号窗口进入了while循环拿到了锁,还没有进行--操作,二号窗口也进入了while循环,因为这时候还有1张票,所以二号窗口可以进入while循环,但是拿不到锁,所以while循环里面的代码执行不了,就只能在while循环里面等待锁的到来,于是1号窗口卖完这最后一张票后,将票数减为0,释放了锁,二号直接拿到这个锁,就可以对0继续--了,于是就得到了-1张票。

        解决方式,就是双重条件判断,在while循环里面再次判断一次票数。

void selltickts(int number)
{
	while (tickts > 0)
	{
		mtx.lock();
		if(tickts>0)
		{
			std::cout << tickts << std::endl;
			tickts--;
		}
		mtx.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//卖一张票花费100ms
	}
}

        运行结果。

 

        这次总算达到了,我们想要的结果。

        但是这段代码还是有一些隐患,假如我while的if语句里面有一个return语句的话,整个程序就变成一个死锁程序了,一个线程拿到锁后并且触发了这个return,就不会释放这个锁了。要解决这个问题,我们需要用到智能锁。

void selltickts(int number)
{
	while (tickts > 0)
	{
		{
			std::lock_guard<std::mutex>loc(mtx);
			if (tickts > 0)
			{
				std::cout <<number<<"号窗口卖出了第"<< tickts << "号票"<<std::endl;
				tickts--;
			}
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(100));//卖一张票花费100ms
	}
}

        由于lock_guard析构是离开一个作用域才会触发的,我们可以给它一个作用域(大括号),代表卖完了这张牌,这把锁就自动释放掉了,这样就可以有效避免死锁问题了。

        运行结果。

         达到了我们想要的效果,三个窗口都在卖票,票数没有异常。

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咩咩大主教

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值