线程互斥及基于线程锁的抢票程序

2024-04-19 03:44
文章标签 互斥 线程 程序 抢票

本文主要是介绍线程互斥及基于线程锁的抢票程序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我们实现一个简单的多线程抢票程序。 

#include<iostream>
#include<thread>
#include<unistd.h>
#include<functional>
#include<vector>
using namespace std;
template<class T>
using func_t=function<void(T)>;//返回值为void,参数为T
template<class T>
class Thread
{public:Thread(func_t<T> func,const string&name,T data):_tid(0),_func(func),_threadname(name),isrunning(false),_data(data){}static void*ThreadRoutine(void*args){//(void)args;//仅仅是为了防止编译器有告警Thread*ts=static_cast<Thread*>(args);ts->_func(ts->_data);return nullptr;}bool Start(){int n=pthread_create(&_tid,nullptr,ThreadRoutine,this);if(n==0){isrunning=true;return true;}return false;}bool Join(){if(!isrunning) return true;int n=pthread_join(_tid,nullptr);if(n==0){isrunning=false;return true;}return false;}string GetThreadName(){return _threadname;}bool IsRunning(){return isrunning;}~Thread(){}private:pthread_t _tid;string _threadname;bool isrunning;func_t<T> _func;T _data;
};
#include"test.hpp"
using namespace std;
string GetThreadName()
{static int number=1;char name[64];snprintf(name,sizeof name,"Thread - %d",number++);return name;
}
void print(int num)
{while(num--){cout<<"hello world"<<num<<endl;sleep(1);}
}
int ticket=100;
void GetTicket(string name)
{while(true){if(ticket>0){usleep(1000);printf("%s get a ticket %d\n",name.c_str(),ticket--);}elsebreak;}}
int main()
{int num=5;//vector<Thread<int>> Threads;string name1=GetThreadName();string name2=GetThreadName();string name3=GetThreadName();string name4=GetThreadName();Thread<string> t1(GetTicket,name1,name1);Thread<string> t2(GetTicket,name2,name2);Thread<string> t3(GetTicket,name3,name3);Thread<string> t4(GetTicket,name4,name4);t1.Start();t2.Start();t3.Start();t4.Start();t1.Join();t2.Join();t3.Join();t4.Join();// while(num--)// {//     Threads.push_back(Thread<int>(print,GetThreadName(),10));// }// for(auto &t:Threads)// {//     cout<<t.GetThreadName()<<" is running? "<<t.IsRunning()<<endl;// }// for(auto&t:Threads)// {//     t.Start();// }// for(auto &t:Threads)// {//     cout<<t.GetThreadName()<<" is running? "<<t.IsRunning()<<endl;// }// for(auto &t:Threads)// {//     t.Join();// }// Thread t(print,GetThreadName());// cout<<"Is thread running?"<<t.IsRunning()<<endl;// t.Start();// cout<<"Is thread running?"<<t.IsRunning()<<endl;// t.Join();return 0;
}

神奇的事情发生了,我们明明添加了ticket大于0时才能抢票的限制条件,

为什么最后ticket会小于0呢? 

判断ticket是否大于0也是访问公共资源,并不是原子的。

可能多个线程同时通过判断,但是在执行ticket--的时候又因时间片变成串行。

最终导致ticket小于0。

数据在内存中,本质是被线程共享的。

数据被读取到寄存器中,本质变成了线程的上下文,属于线程的私有数据。

我们把任何一个时刻,只允许一个线程访问的共享资源叫做临界资源。

进程中访问临界资源的代码就叫做临界区。

任何时刻,互斥保证只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用。

原子性:不会被任何调度打断的操作,只有两态,完成或未完成。

我们认为,一条汇编语句就是原子的。

而像a++这样的C语言指令,实际上会被转化成三条汇编指令。

首先把a从内存拷贝到CPU的寄存器,字CPU中完成++操作,再返回内存,不是原子的。

为了不让上面的情况发生,需要加锁。 

 

 如图,加锁后,问题解决,不过速度变慢了。

申请锁本身是原子的,有宝马。这块的原理下一篇博客会介绍。

根据互斥的定义,任何一个时刻只允许一个线程申请锁成功,申请失败的进程在mutex阻塞,本质就是等待。

可能大家会认为,当线程进入锁,执行锁内代码,这是如果发生切换,还是会产生同样的错误。

其实这次不一样了,因为就算时间片到了,发生线程切换,被切换到的线程也没办法访问锁。

它在申请锁那里阻塞着呢。。。

这就保证了临界区的原子性。

就相当于上的线程的密码锁,专属的。

这篇关于线程互斥及基于线程锁的抢票程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/916555

相关文章

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

Java子线程无法获取Attributes的解决方法(最新推荐)

《Java子线程无法获取Attributes的解决方法(最新推荐)》在Java多线程编程中,子线程无法直接获取主线程设置的Attributes是一个常见问题,本文探讨了这一问题的原因,并提供了两种解决... 目录一、问题原因二、解决方案1. 直接传递数据2. 使用ThreadLocal(适用于线程独立数据)

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

EMLOG程序单页友链和标签增加美化

单页友联效果图: 标签页面效果图: 源码介绍 EMLOG单页友情链接和TAG标签,友链单页文件代码main{width: 58%;是设置宽度 自己把设置成与您的网站宽度一样,如果自适应就填写100%,TAG文件不用修改 安装方法:把Links.php和tag.php上传到网站根目录即可,访问 域名/Links.php、域名/tag.php 所有模板适用,代码就不粘贴出来,已经打

跨系统环境下LabVIEW程序稳定运行

在LabVIEW开发中,不同电脑的配置和操作系统(如Win11与Win7)可能对程序的稳定运行产生影响。为了确保程序在不同平台上都能正常且稳定运行,需要从兼容性、驱动、以及性能优化等多个方面入手。本文将详细介绍如何在不同系统环境下,使LabVIEW开发的程序保持稳定运行的有效策略。 LabVIEW版本兼容性 LabVIEW各版本对不同操作系统的支持存在差异。因此,在开发程序时,尽量使用

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

这些心智程序你安装了吗?

原文题目:《为什么聪明人也会做蠢事(四)》 心智程序 大脑有两个特征导致人类不够理性,一个是处理信息方面的缺陷,一个是心智程序出了问题。前者可以称为“认知吝啬鬼”,前几篇文章已经讨论了。本期主要讲心智程序这个方面。 心智程序这一概念由哈佛大学认知科学家大卫•帕金斯提出,指个体可以从记忆中提取出的规则、知识、程序和策略,以辅助我们决策判断和解决问题。如果把人脑比喻成计算机,那心智程序就是人脑的

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api