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

embedded/2024/10/18 13:35:57/

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

#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.ppmy.cn/embedded/8953.html

相关文章

FreeLearning PHP 译文集翻译完成

使用 PHP 和 jQuery 构建游戏化 Web 站点使用 PHP7 构建 REST Web 服务PHP 入门指南CouchDB 和 PHP Web 开发初学者指南Vue2 和 Laravel5 全栈开发函数式 PHPAngular6 和 Laravel5 Web 全栈开发实用指南FuelPHP 高效开发学习手册PHP 数据对象学习手册PHP7 高性能开发学习手册La…

MySQL--创建,删除,查找,案例

1.数据库的---创建&#xff0c;删除&#xff0c;查找&#xff0c;案例 create database 数据库名称; # 创建一个数据库&#xff0c;所有参数默认 create database 数据库名称 [default chasetutf8mb4] # 创建的同时指定了编码2.drop删除 drop database 数据库名称;3.进入数据库…

php ArrayAccess

class Foo implements ArrayAccess {public function offsetExists( $offset ) {echo "这里是 offsetExists() 方法 你输入的参数是 {$offset}";}public function offsetGet( $offset ) {echo "这里是 offsetGet() 方法 你输入的参数是 $offset";}public f…

ubuntu如何查找某个时间段的日志信息?

日志示例&#xff1a; 2024-04-15 14:06:32,523 1161 INFO B8 odoo.addons.base.models.ir_cron: Job Post process payment transactions done. 根据提供的日志示例&#xff0c;日期格式为YYYY-MM-DD HH:MM:SS,sss。基于这个日期格式&#xff0c;以下是一条可以筛选今天9:40到…

解决zabbix中文乱码问题

目录 1、遇到的问题 2、解决方法 第一步&#xff1a;在windows电脑上可以搜索simkai.ttf文件&#xff0c;上传到 /usr/share/zabbix/assets/fonts文件夹 第二步&#xff1a;删除软链接 第三步&#xff1a;创建软链接 第四步:重启服务 3、检查问题是否被解决 1、遇到的问…

【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Push Button的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 按钮类控件 | Push Button的使用及说明 文章编号&#x…

嵌入式学习57-ARM6(内核编译)

知识零碎&#xff1a; arm2440 精简指令集架构 …

智能家居如何融合人工智能技术

随着科技的飞速发展&#xff0c;智能家居已经成为了现代家庭的一个重要组成部分。而人工智能技术的应用&#xff0c;则使得智能家居更加智能化、便捷化和个性化。让我们一起来探讨智能家居如何融合人工智能技术&#xff0c;为我们的生活带来更多的便利和舒适。 1. 智能语音助手…