【Linux】详解加锁实现线程互斥

一、多线程不加线程互斥可能会引发的问题

        下面是一个抢标逻辑。抢票为什么会抢到负数:假设当票数为1时,此时四个进程的判断条件tickets都大于0,都会进入抢票操作,第一个进程抢完票以后tickets==0并写回内存,第二个进程再从内存中读取tickets的值时此时tickets已经为0,再做--就变成了-1,tickets为负数就是这么来的。也就是说,多线程代码如果不对共享资源做保护可能会有并发问题。

二、互斥锁

2.1、静态分配锁

如果你定义的锁是静态的或者是全局的,可以直接初始化成

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

2.2、动态分配锁销毁锁

int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
参数mutex:要初始化的互斥量,attr: NULL。
int pthread_mutex_destroy(pthread_mutex_t *mutex)

注意: 使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要销毁

2.3、加锁解锁

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值:成功返回0,失败返回错误号 。 pthread_mutex_lock函数如果申请锁成功就会继续向后运行,如果申请失败该函数就会阻塞不允许继续向后运行。

加锁的粒度要越细越好

三、加锁的底层理解

movb $0,%al表示将0存入%al寄存器中(%al是累加寄存器AX的低8位部分,可以独立作为8位寄存器使用。), xchgb %al, mutex表示交换%al寄存器中的值和内存mutex中的值,如果内存mutex中的值原本是1,交换完则表示得到锁,否则挂起等待。unlock中将1存入mutex内存中表示归还锁。这样无论如何,得到1的线程始终只会有一个,也就做到了线程互斥。

 四、多线程实现简单的互斥抢票

//thread.hpp
#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>namespace ThreadModule
{template<typename T>using func_t = std::function<void(T)>;// typedef std::function<void(const T&)> func_t;template<typename T>class Thread{public:void Excute(){_func(_data);}public:Thread(func_t<T> func, T data, const std::string &name="none-name"): _func(func), _data(data), _threadname(name), _stop(true){}//记住此方法static void *threadroutine(void *args) // 类成员函数,形参是有this指针的!!{Thread<T> *self = static_cast<Thread<T> *>(args);self->Excute();return nullptr;}bool Start(){int n = pthread_create(&_tid, nullptr, threadroutine, this);if(!n){_stop = false;return true;}else{return false;}}void Detach(){if(!_stop){pthread_detach(_tid);}}void Join(){if(!_stop){pthread_join(_tid, nullptr);}}std::string name(){return _threadname;}void Stop(){_stop = true;}~Thread() {}private:pthread_t _tid;std::string _threadname;T _data;func_t<T> _func;bool _stop;};
} #endif
//LockGuard.hpp
#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__#include <iostream>
#include <pthread.h>class LockGuard
{
private:pthread_mutex_t* _mutex;
public:LockGuard( pthread_mutex_t* mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}~LockGuard(){pthread_mutex_unlock(_mutex);}};#endif
//testThread.cc
#include <iostream>
#include <vector>
#include "LockGuard.hpp"
#include "Thread.hpp"
using namespace ThreadModule;int g_tickets = 10000;
const int num = 4;class ThreadData
{
public:int &_tickets; // 所有的线程,最后都会引用同一个全局的g_ticketsstd::string _name;int _total;pthread_mutex_t &_mutex;public:ThreadData(int &tickets, const std::string &name, pthread_mutex_t &mutex): _tickets(tickets), _name(name), _total(0), _mutex(mutex){}~ThreadData(){}
};void route(ThreadData *td)
{while (true){LockGuard guard(&td->_mutex);if (td->_tickets > 0){usleep(1000);printf("%s running, get tickets: %d\n", td->_name.c_str(), td->_tickets); td->_tickets--;                                                          td->_total++;}elsebreak;}
}int main()
{pthread_mutex_t mutex;pthread_mutex_init(&mutex,nullptr);std::vector<Thread<ThreadData*>> threads;std::vector<ThreadData *> datas;//1、创建一批线程for(int i = 0; i<num; i++){std::string name = "thread-" + std::to_string(i+1);ThreadData* td = new ThreadData(g_tickets, name, mutex);threads.emplace_back(route, td, name);datas.emplace_back(td);}// 2. 启动 一批线程for (auto &thread : threads){thread.Start();}// 3. 等待一批线程for (auto &thread : threads){thread.Join();}sleep(1);// 4. 输出统计数据for (auto data : datas){std::cout << data->_name << " : " << data->_total << std::endl;delete data;}pthread_mutex_destroy(&mutex);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/47515.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Xcode进行真机测试时总是断连,如何解决?

嗨。大家好&#xff0c;我是兰若姐姐。最近我在用真机进行app自动化测试的时候&#xff0c;经常会遇到xcode和手机断连&#xff0c;每次断连之后需要重新连接&#xff0c;每次断开都会出现以下截图的报错 当这种情况出现时&#xff0c;之前执行的用例就相当于白执行了&#xff…

Adobe XD中文设置指南:专业设计师的现场解答

Adobe XD是世界领先的在线合作UI设计工具。它摆脱了Sketch、Figma等传统设计软件对设备的依赖&#xff0c;使设计师可以随时随地使用任何设备打开网页浏览器&#xff0c;轻松实现跨平台、跨时空的设计合作。然后&#xff0c;为了提高国内设计师的使用体验&#xff0c;Adobe XD如…

爬虫(二)——爬虫的伪装

前言 本文是爬虫系列的第二篇文章&#xff0c;主要讲解关于爬虫的简单伪装&#xff0c;以及如何爬取B站的视频。建议先看完上一篇文章&#xff0c;再来看这一篇文章。要注意的是&#xff0c;本文介绍的方法只能爬取免费视频&#xff0c;会员视频是无法爬取的哦。 爬虫的伪装 …

C++类与对象(补)

感谢大佬的光临各位&#xff0c;希望和大家一起进步&#xff0c;望得到你的三连&#xff0c;互三支持&#xff0c;一起进步 个人主页&#xff1a;LaNzikinh-CSDN博客 文章目录 前言一.默认成员函数二.static三.友元四.匿名对象总结 前言 类的默认成员函数&#xff0c;默认成员…

linux centos limits.conf 修改错误,无法登陆问题修复 centos7.9

一、问题描述 由于修改/etc/security/limits.conf这个文件中的值不当&#xff0c;重启后会导致其账户无法远程登录&#xff0c;本机登录。 如改成这样《错误示范》&#xff1a; 会出现&#xff1a; 二、解决 现在知道是由于修改limits.conf文件不当造成的&#xff0c;那么就…

Android-- 集成谷歌地图

引言 项目需求需要在谷歌地图&#xff1a; 地图展示&#xff0c;设备点聚合&#xff0c;设备站点&#xff0c;绘制点和区域等功能。 我只针对我涉及到的技术做一下总结&#xff0c;希望能帮到开始接触谷歌地图的伙伴们。 集成步骤 1、在项目的modle的build.gradle中添加依赖如…

p19 C语言操作符详解

算术操作符 1.除了%操作符之外&#xff0c;其他的几个操作符可以作用于整数和浮点数。 2.对于/操作符如果两个操作数都为整数&#xff0c;执行整数除法。而只要有浮点数值型的就是浮点除法。 3.%操作符的两个操作数必须为 整数。返回的是整除之后的余数。 #include<std…

计算机毕业设计-基于Springboot的养老院管理系统-源码程序文档

项目源码&#xff0c;请关注❥点赞收藏并私信博主&#xff0c;谢谢~ 本系统开发采用技术为JSP、Bootstrap、Ajax、SSM、Java、Tomcat、Maven 此文章为本人亲自指导加编写&#xff0c;禁止任何人抄袭以及各类盈利性传播&#xff0c; 相关的代码部署论文ppt代码讲解答辩指导文件…

SpringBoot以及swagger的基本使用

1、SpringBoot是什么&#xff1f; 一种快速开发、启动Spring的框架、脚手架 遵循“约定优于配置”的思想&#xff0c;使得能够快速创建和配置Spring应用 2、SpringBoot的核心特性 自动配置&#xff0c;一些依赖、默认配置都预设好了&#xff0c;减少了配置量起步依赖&#x…

表格竖向展示

最近在做手机端web页面&#xff0c;页面中需要有个表格来显示数据&#xff0c;但是由于数据太多页面太窄&#xff0c;table展示横向滑动的话感觉很丑。所以让表格竖向显示了 具体页面如下: 实现代码&#xff1a;当然代码里面绑定的数据啊什么的你都可以修改为自己的内容&#…

【文献阅读】Social Bot Detection Based on Window Strategy

Abstract 机器人发帖的目的是在不同时期宣传不同的内容&#xff0c;其发帖经常会出现异常的兴趣变化、而人类发帖的目的是表达兴趣爱好和日常生活&#xff0c;其兴趣变化相对稳定。提出了一种基于窗口策略&#xff08;BotWindow Strategy&#xff09;的社交机器人检测模型基于…

【fastadmin】selectpickers多选提交后数据库只保存了一个选择值

问题描述 在 fastadmin 框架开发项目中&#xff0c;用到了selectpickers多选组件。 例如&#xff1a;选择了两位员工&#xff0c;但是提交后数据库只保存了一个选择值。 <div class"form-group"><label class"control-label col-xs-12 col-sm-2"…

Puppeteer动态代理实战:提升数据抓取效率

引言 Puppeteer是由Google Chrome团队开发的一个Node.js库&#xff0c;用于控制Chrome或Chromium浏览器。它提供了高级API&#xff0c;可以进行网页自动化操作&#xff0c;包括导航、屏幕截图、生成PDF、捕获网络活动等。在本文中&#xff0c;我们将重点介绍如何使用Puppeteer…

【深度学习】InternVL2-8B,图转文,docker部署

文章目录 基础fastapi服务请求fastapi接口 基础 https://huggingface.co/OpenGVLab/InternVL2-8B#%E7%AE%80%E4%BB%8B InternVL2-26B应该更好&#xff0c;但显存没那么大&#xff0c;只能跑InternVL2-8B了。 下载&#xff1a; cd /ssd/xiedong/InternVL2-26B git clone htt…

unity3d脚本使用start,updata,awake

最近学了一下unity&#xff0c;脚本编写用的c#&#xff0c;虽说没学过c#但是勉强根据教学还能写点代码。 在这里我来记录一下在我学习过程中感觉最重要的东西 消息函数&#xff1a; 在我们创建一个脚本文件的时候&#xff0c;我们首先可以看到两个默认给你写出来的函数。 这两…

RAG技术-为自然语言处理注入新动力

引言&#xff1a; 在自然语言处理&#xff08;NLP&#xff09;的领域中&#xff0c;RAG&#xff08;Retrieval-Augmented Generation&#xff09;技术以其独特的方式&#xff0c;正在改变我们与机器的交互方式。RAG技术结合了大语言模型的强大能力&#xff0c;使得机器在理解和…

MongoDB教程(九):java集成mongoDB

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、环境准…

SAPUI5基础知识15 - 理解控件的本质

1. 背景 经过一系列的练习&#xff0c;通过不同的SAPUI5控件&#xff0c;我们完成了对应用程序界面的初步设计&#xff0c;在本篇博客中&#xff0c;让我们一起总结下SAPUI5控件的相关知识点&#xff0c;更深入地理解SAPUI5控件的本质。 通常而言&#xff0c;一个典型UI5应用…

RocketMQ单结点安装/Dashboard安装

目录 1.安装NameServer 2.安装Broker 3.使用自带工具测试数据发送 4.使用DashBoard进行查看 5.关闭相关设备 前置条件&#xff1a;两台虚拟机CentOS Linux release 7.5.1804(ps:当然也可以都部署在一台机器上) RocketMq属于天生集群。需要同时启动nameServer和Broker进行…

CMMI认证是什么?做CMMI认证的必要条件?CMMI认证的重要性

CMMI&#xff08;Capability Maturity Model Integration&#xff09;认证&#xff0c;作为企业追求卓越软件工程实践的标志&#xff0c;其实现过程既严谨又复杂。首先&#xff0c;我们需要深入理解CMMI的核心理念&#xff0c;即持续的过程改进和成熟度提升。 为了实现CMMI认证…