当前位置: 首页 > news >正文

Linux中的锁

user2正在进行抢票: 4
user3正在进行抢票: 3
user1正在进行抢票: 2
user4正在进行抢票: 1
user2正在进行抢票: 0
user3正在进行抢票: -1
user1正在进行抢票: -2 

int tickets=10000;
void* getTicket(void* args)
{string username=static_cast<const char*>(args);while(true){if(tickets>0){usleep(1000);//1s=10^3ms=10^6us=10^nscout<<username<<"正在进行抢票: "<<tickets--<<endl;}else{break;}}
}
int main()
{unique_ptr<Thread> thread1(new Thread(getTicket,(void*)"user1",1));unique_ptr<Thread> thread2(new Thread(getTicket,(void*)"user2",2));unique_ptr<Thread> thread3(new Thread(getTicket,(void*)"user3",3));unique_ptr<Thread> thread4(new Thread(getTicket,(void*)"user4",4));   thread1->join();thread2->join();thread3->join();return 0;
}
取出ticket--部分的汇编代码 
objdump -d a.out > test.objdump 
152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax # 600b34 <ticket> 
153 400651: 83 e8 01 sub $0x1,%eax 
154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip) # 600b34 <ticket> 

-- 操作并不是原子操作,而是对应三条汇编指令:

 load :将共享变量ticket从内存加载到寄存器中

update : 更新寄存器里面的值,执行-1操作

store :将新值,从寄存器写回共享变量ticket的内存地址

要解决以上问题,需要做到三点:

代码必须要有互斥行为:当代码进入临界区执行时,不允许其他线程进入该临界区。

如果多个线程同时要求执行临界区的代码,并且临界区没有线程在执行,那么只能允许一个线程进入该临 界区。

如果线程不在临界区中执行,那么该线程不能阻止其他线程进入临界区。 要做到这三点,本质上就是需要一把锁。

Linux上提供的这把锁叫互斥量。

锁的初始化和销毁:

将锁定义为局部变量:

pthread_mutex_t lock;
pthread_mutex_init(&lock,nullptr);
pthread_mutex_destroy(&lock);

将锁定义为全局变量:

int tickets=10000;
pthread_mutex_t lock=PTHREAD_MUTEX_INITALIZER;

tickets是一个全局变量被多个线程同时访问时,将该变量称为共享数据,共享数据进过锁的保护称为临界资源,可以保证安全进行访问。

#pragma once #include<iostream>
#include<pthread.h>class Mutex
{
public:Mutex(pthread_mutex_t* lock_p=nullptr):lock_p_(lock_p){}void lock(){if(lock_p_) pthread_mutex_lock(lock_p_);}void unlock(){if(lock_p_) pthread_mutex_unlock(lock_p_);}~Mutex(){}
private:pthread_mutex_t* lock_p_;
};
class LockGuard
{
public://构造函数LockGuard(pthread_mutex_t* mutex):mutex_(mutex){mutex_.lock();}~LockGuard(){mutex_.unlock();}
private:Mutex mutex_;
};
int tickets=10000;
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER; 
class ThreadData
{
public:ThreadData(const string& threadname,pthread_mutex_t* mutex_p):threadname_(threadname),mutex_p_(mutex_p){}~ThreadData() {}string threadname_;pthread_mutex_t* mutex_p_;
};void* getTicket(void* args)
{//string username=static_cast<const char*>(args);ThreadData* td=static_cast<ThreadData*>(args);while(true){// pthread_mutex_lock(td->mutex_p_);//pthread_mutex_lock(&lock);{LockGuard lockguard(&lock);if(tickets>0){usleep(1000);//1s=10^3ms=10^6us=10^nscout<<td->threadname_<<"正在进行抢票: "<<tickets<<endl;tickets--;pthread_mutex_unlock(&lock);}else{pthread_mutex_unlock(&lock);break;}}//cout<<"我是一个新线程,我正在做: "<<work_type<<endl;// sleep(1);//抢完票之后还需要形成订单给用户usleep(1000);}
}

将临界区封装为代码块:

{LockGuard lockguard(&lock);if(tickets>0){usleep(1000);//1s=10^3ms=10^6us=10^nscout<<td->threadname_<<"正在进行抢票: "<<tickets<<endl;tickets--;pthread_mutex_unlock(&lock);}else{pthread_mutex_unlock(&lock);break;}}

大大提高了加锁效率。

如何看待锁?

a.锁本身就是一个共享资源!!!

b.pthread_mutex_lock:加锁过程必须是安全的!!!加锁过程其实是原子的。

c.如果申请成功就继续向后执行,如果申请暂时没有成功,执行流会阻塞。

d.谁持有锁谁进入临界区

加锁的过程是原子的。

互斥量实现原理探究

经过上面的例子,大家已经意识到单纯的 i++ 或者 ++i 都不是原子的,有可能会有数据一致性问题 为了实现互斥锁操作,大多数体系结构都提供了swap或exchange指令,该指令的作用是把寄存器和内存单 元的数据相交换,由于只有一条指令,保证了原子性,即使是多处理器平台,访问内存的 总线周期也有先后,一 个处理器上的交换指令执行时

 

 常见不可重入的情况

调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的

调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构

可重入函数体内使用了静态的数据结构

死锁

死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资 源而处于的一种永久等待状态。

死锁的四个必要条件:

1.互斥

2.请求与 保持

3.不剥夺

4.环路等待条件

避免死锁

破坏死锁的四个必要条件

加锁顺序一致

避免锁未释放的场景

资源一次性分配

Linux线程同步

条件变量

当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了。

例如一个线程访问队列时,发现队列为空,它只能等待,只到其它线程将一个节点添加到队列中。这种情 况就需要用到条件变量。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 预计下半年业务将反弹回升,亚信科技的底气源自哪里?
  • MySQL——单表查询(二)按条件查询(4)空值查询
  • 《深入浅出多模态》(八)多模态经典模型:MiniGPT4
  • qt-16可扩展对话框--隐藏和展现
  • 【硬件模块】震动传感器模块
  • Python做统计图之美
  • 注意!美国跨境选品风向变动,低价产品反成抢手货!
  • vos3000怎样对接voip落地语音网关呢?卡机和O口网关的配置技巧有哪些?
  • 牛客JS题(四十五)数组去重
  • Element-05.组件-Form表单
  • JavaSE-详细介绍
  • Python 环境搭建指南 超详细
  • SpringBoot自动配置
  • vscode 远程免密登录
  • springCloudAlibaba整合log4j2
  • Cumulo 的 ClojureScript 模块已经成型
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • Transformer-XL: Unleashing the Potential of Attention Models
  • use Google search engine
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 新书推荐|Windows黑客编程技术详解
  • 学习笔记TF060:图像语音结合,看图说话
  • 一天一个设计模式之JS实现——适配器模式
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 关于Android全面屏虚拟导航栏的适配总结
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​configparser --- 配置文件解析器​
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • ### RabbitMQ五种工作模式:
  • #职场发展#其他
  • (003)SlickEdit Unity的补全
  • (13)Hive调优——动态分区导致的小文件问题
  • (33)STM32——485实验笔记
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (多级缓存)多级缓存
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (万字长文)Spring的核心知识尽揽其中
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .net web项目 调用webService
  • .NET 中创建支持集合初始化器的类型
  • .net开发引用程序集提示没有强名称的解决办法
  • .vollhavhelp-V-XXXXXXXX勒索病毒的最新威胁:如何恢复您的数据?
  • .考试倒计时43天!来提分啦!
  • [100天算法】-实现 strStr()(day 52)
  • [2023年]-hadoop面试真题(一)
  • [acm算法学习] 后缀数组SA