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

Linux 第三十四章

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++,linux

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

封装线程

Linux线程互斥

加锁 

创建一个锁(互斥量)

pthread_mutex_lock(&mutex);加锁/pthread_mutex_unlock(&mutex);解锁

源码实现


封装线程

//开发方
#pragma once#include<iostream>
#include<string>
#include<functional>
#include<pthread.h>using namespace std;//typedef function<void()> func_t
template<class T>using func_t=function<void(T)>;template<class T>class Thread{
public:
Thread(func_t<T> func,const string& threadname,T data):_tid(0),_threadname(threadname),_isrunning(false),_func(func),_data(data)
{}static void* Threadroutine(void* args)//类内成员方法,其第一个参数是this指针,所以会导致编译错误//这里使用static,让Thraedroutine成为类的方法,
{
(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;}
else return false;}bool Join(){
if(!_isrunning)return true;int n=pthread_join(_tid,nullptr);if(n==0)
{
_isrunning=false;
return true;}
return false;}bool Isrunning(){
return _isrunning;}string Threadname(){
return _threadname;}
private:
pthread_t _tid;string _threadname;bool _isrunning;func_t<T> _func;T _data;};//应用方
#include<iostream>
#include"thread.hpp"
#include<unistd.h>
#include<vector>void Print(int num){
while(num)
{
cout<<"hello world :"<<num--<<endl;
sleep(1);
}
}string Getthreadname(){
static int number=1;//全局变量char name[64];snprintf(name,sizeof(name),"Thread-%d",number++);
return name;}int main(){
//**1**
// Thread t(Print,Getthreadname());
// cout<<"is thread running?"<<t.Isrunning()<<endl;
// t.Start();
// cout<<"is thread running?"<<t.Isrunning()<<endl;
// t.Join();//**2**
// const int num=5;
// vector<Thread> threads;// for(int i=0;i<num;i++)
// {
// Thread t(Print,Getthreadname());
// threads.push_back(t);
// }// for( auto& thread:threads)
// {
// cout<<thread.Threadname()<<"is running: "<<thread.Isrunning()<<endl;
// }// for(auto& thread:threads)
// {
// thread.Start();
// }// for( auto& thread:threads)
// {
// cout<<thread.Threadname()<<"is running: "<<thread.Isrunning()<<endl;
// }// //不让主线程退出,让主线程等待子线程
// for( auto& thread:threads)
// {
// thread.Join();
// }//**3**Thread<int> t(Print,Getthreadname(),10);t.Start();
t.Join();return 0;}

Linux线程互斥

1.直接实验?(不确定性高)

多线程抢票的逻辑

抢票系统

Thread-4 get a ticket:0 

Thread-1 get a ticket:-1 

Thread-2 get a ticket:-2 

票被抢到负数是不合理的

2.看到现象—输出概念

1)数据不一致,共享资源(票)

2)任何一个时刻,只允许一个线程正在访问的资源——临界资源(互斥)

3)我们把我们进程中访问临界资源的代码——临界区(被保护起来的重点区域)

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

3.解释问题

int cnt=0;

cnt++; 

这种操作不是原子的

抢票中的临界区

抢票中的临界区
int ticket = 10000;
void Getticket(string name)
{
while (true)
{
if (ticket > 0)//判断ticket也是计算
{
usleep(1000); // 模拟抢票的话费时间
printf("%s get a ticket:%d \n", name.c_str(), ticket);
ticket—;//在汇编层面,会执行三步
//我的理解:读是在每个线程的上下文数据中,—是在内存中
}
else
break;
}
// 实际情况还有后续的动作
}

CPU的基本功能
算:算数运算
逻:逻辑运算
中:处理内外中断
控:控制单元

加锁 

加锁(牺牲效率为代价的,解决安全性问题)

1.我们要尽可能的给少的代码加锁

2.一般加锁,都是给临界区加锁

根据互斥的定义,任何时刻,只允许一个线程申请锁成功!多个线程申请锁的失败,失败的线程怎么办?在mutex上阻塞,(等待其他线程释放掉锁,再被唤醒申请锁)

申请锁本身是安全的,原子的(只能一个线程申请成功)

一个线程在临界区中访问临界资源的时候,可不可能发生切换?

可能,完全允许,因为该线程被切换,但是没有解锁,其他线程就申请不到锁,只能被阻塞(就不会发生并发访问数据不一致的问题)

创建一个锁(互斥量)

pthread_mutex_t mutex;

pthread_mutex_lock(&mutex);加锁/pthread_mutex_unlock(&mutex);解锁

在Linux系统中,pthread_mutex_lock 是一个 POSIX 线程库提供的函数,用于加锁互斥量(mutex)。它的作用是尝试锁定一个互斥量,如果这个互斥量已经被其他线程锁定了,则调用线程会被阻塞直到获取到该互斥量为止。

事例

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex;int shared_data = 0;//公共资源void* thread_function(void* arg) {// 加锁pthread_mutex_lock(&mutex);// 访问共享资源shared_data++;printf("Thread ID %ld, shared data: %d\n", pthread_self(), shared_data);// 解锁pthread_mutex_unlock(&mutex);return NULL;
}int main() {pthread_t tid1, tid2;// 初始化互斥量if (pthread_mutex_init(&mutex, NULL) != 0) {perror("Mutex initialization failed");exit(EXIT_FAILURE);}// 创建线程pthread_create(&tid1, NULL, thread_function, NULL);pthread_create(&tid2, NULL, thread_function, NULL);// 等待线程结束pthread_join(tid1, NULL);pthread_join(tid2, NULL);// 销毁互斥量pthread_mutex_destroy(&mutex);return 0;
}
在上面的示例中,我们首先创建了一个互斥量 pthread_mutex_t mutex,并在主线程中初始化它。然后创建了两个线程,它们会执行 thread_function 函数。在 thread_function 中,通过调用 pthread_mutex_lock 来锁定互斥量,确保对 shared_data 的访问是互斥的,然后进行相应操作,最后再用 pthread_mutex_unlock 解锁。
这样就确保了在任意时刻只有一个线程能够访问 shared_data,避免了数据竞争问题。请注意,对于互斥量的使用需要谨慎处理,以避免死锁等问题。

源码实现

main

#include <iostream>#include "thread.hpp"#include <unistd.h>#include <vector>void Print(int num){
while (num){
cout << "hello world :" << num-- << endl;sleep(1);
}
}string Getthreadname(){
static int number = 1; // 全局变量char name[64];snprintf(name, sizeof(name), "Thread-%d", number++);return name;}int ticket = 10000;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 锁有了,被定义初始化,这是一把全局的锁// 加锁(牺牲效率为代价的,解决安全性问题)
// 1.我们要尽可能的给少的代码加锁
// 2.一般加锁,都是给临界区加锁
void Getticket(string name){
while (true){
// 加锁
// 根据互斥的定义,任何时刻,只允许一个线程申请锁成功!多个线程申请锁的失败,失败的线程怎么办?在mutex上阻塞,(等待其他线程释放掉锁,再被唤醒申请锁)
pthread_mutex_lock(&mutex); // 申请锁本身是安全的,原子的(只能一个线程申请成功)// 一个线程在临界区中访问临界资源的时候,可不可能发生切换?
// 可能,完全允许,因为该线程被切换,但是没有解锁,其他线程就申请不到锁,只能被阻塞
//
if (ticket > 0){
usleep(1000); // 模拟抢票的话费时间printf("%s get a ticket:%d \n", name.c_str(), ticket);ticket--;
// 解锁
pthread_mutex_unlock(&mutex);
}
else
{
// 解锁
pthread_mutex_unlock(&mutex);
break;
}
}
// 实际情况还有后续的动作
}int main(){
string name1 = Getthreadname();Thread<string> t1(name1, Getticket, name1);string name2 = Getthreadname();Thread<string> t2(name2, Getticket, name2);string name3 = Getthreadname();Thread<string> t3(name3, Getticket, name3);string name4 = Getthreadname();Thread<string> t4(name4, Getticket, name4);t1.Start();
t2.Start();
t3.Start();
t4.Start();t1.Join();
t2.Join();
t3.Join();
t4.Join();
return 0;}

thread.hpp

#pragma once#include<iostream>
#include<string>
#include<functional>
#include<pthread.h>using namespace std;//typedef function<void()> func_t
template<class T>using func_t=function<void(T)>;template<class T>class Thread{
public:
Thread(const string& threadname,func_t<T> func,T data):_tid(0),_threadname(threadname),_isrunning(false),_func(func),_data(data)
{}static void* Threadroutine(void* args)//类内成员方法,其第一个参数是this指针,所以会导致编译错误//这里使用static,让Thraedroutine成为类的方法,
{
(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;}
else return false;}bool Join(){
if(!_isrunning)return true;int n=pthread_join(_tid,nullptr);if(n==0)
{
_isrunning=false;
return true;}
return false;}bool Isrunning(){
return _isrunning;}string Threadname(){
return _threadname;}
private:
pthread_t _tid;string _threadname;bool _isrunning;func_t<T> _func;T _data;};

进程是资源的分配单位,所以线程并不拥有系统资源,而是共享使用进程的资源,进程的资源由系统进行分配


pthread_self() 用于获取用户态线程的tid,而并非轻量级进程ID

主线程调用pthread_cancel(pthread_self())函数来退出自己, 则主线程对应的轻量级进程状态变更成为Z, 其他线程不受影响,这是正确的(正常情况下我们也不会这么做....)
主线程调用pthread_exit只是退出主线程,并不会导致进程的退出

 🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸   

相关文章:

Linux 第三十四章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…...

国际化日期(inti)

我们可以使用国际化API自动的格式化数字或者日期&#xff0c;并且格式化日期或数字的时候是按照各个国家的习惯来进行格式化的&#xff0c;非常的简单&#xff1b; const now new Date(); labelDate.textContent new Intl.DateTimeFormat(zh-CN).format(now);比如说这是按照…...

【论文阅读笔记】jTrans(ISSTA 22)

个人博客地址 [ISSTA 22] jTrans&#xff08;个人阅读笔记&#xff09; 论文&#xff1a;《jTrans: Jump-Aware Transformer for Binary Code Similarity》 仓库&#xff1a;https://github.com/vul337/jTrans 提出的问题 二进制代码相似性检测&#xff08;BCSD&#xff0…...

单位个人如何向期刊投稿发表文章?

在单位担任信息宣传员一职以来,我深感肩上的责任重大。每月的对外信息宣传投稿不仅是工作的核心,更是衡量我们部门成效的重要指标。起初,我满腔热血,以为只要勤勉努力,将精心撰写的稿件投至各大报社、报纸期刊的官方邮箱,就能顺利登上版面,赢得读者的青睐。然而,现实远比理想骨…...

Redis数据结构-RedisObject

1.7 Redis数据结构-RedisObject Redis中的任意数据类型的键和值都会被封装为一个RedisObject&#xff0c;也叫做Redis对象&#xff0c;源码如下&#xff1a; 1、什么是redisObject&#xff1a; 从Redis的使用者的角度来看&#xff0c;⼀个Redis节点包含多个database&#xff…...

Vue 中使用 el-date-picker 限制只能选择当天、当天之前或当天之后日期的方法详解

网上很多都是不完整的&#xff0c;我这里发布一个完整的 - 8.64e7 表示可选择当天时间&#xff08;注&#xff1a;小于当前时间&#xff0c;- 8.64e7 则是禁用日期不包含当前日&#xff0c;若大于当前日期&#xff0c; 8.64e7 则是禁用日期包含当前日&#xff09; time.getTi…...

系列介绍:《创意代码:Processing艺术编程之旅》

系列介绍&#xff1a;《创意代码&#xff1a;Processing艺术编程之旅》 标题创意&#xff1a; “代码绘梦&#xff1a;Processing艺术编程入门”“数字画布&#xff1a;用Processing创造视觉奇迹”“编程美学&#xff1a;Processing艺术创作指南”“创意编程&#xff1a;Proc…...

深度学习设计模式之抽象工厂模式

文章目录 前言一、介绍二、详细分析1.核心组成2.实现步骤3.代码示例4.优缺点优点缺点 5.使用场景 总结 前言 本文主要学习抽象工厂模式&#xff0c;抽象工厂模式创建的是对象家族&#xff0c;比如&#xff1a;苹果是一个产品&#xff0c;但是他不单单只生产手机&#xff0c;还…...

K8s是什么?

url address K8s是一个开源的容器编排平台&#xff0c;可以自动化&#xff0c;在部署&#xff0c;管理和扩展容器化应用过程中涉及的许多手动操作。 Kubernetes最初是由Google工程师作为Borg项目开发和设计的&#xff0c;后于2015年捐赠给云原生计算基金会&#xff08;CNCF&a…...

【网站项目】SpringBoot796水产养殖系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...

Vue详细介绍

Vue.js&#xff08;通常简称为Vue&#xff09;是一个用于构建用户界面的渐进式JavaScript框架。它由尤雨溪&#xff08;Evan You&#xff09;创建&#xff0c;并于2014年首次发布。Vue的设计目的是易于上手&#xff0c;同时也能够强大到驱动复杂的单页应用&#xff08;SPA&…...

声纹识别的对抗与防御

随着机器学习理论和方法的发展, 出现了用于模仿特定说话人语音的深度伪造、针对语音识别和声纹识别的对抗样本, 它们都为破坏语音载体的可信性和安全性提供了具体手段, 进而对各自应用场景的信息安全构成了挑战。 深度伪造是利用生成式对抗网络等方法, 通过构建特定的模型, 产生…...

C++ QT设计模式总结

Ciallo&#xff5e;(∠・ω< )⌒★&#xff0c;这里是 Eureka&#xff0c;欢迎来看我的设计模式总结&#xff0c;有问题随时可以告诉我~ 面向对象的设计模式&#xff1a; 以下总结了面向对象的设计模式&#xff0c; QT 的源码在哪里体现了这些模式&#xff0c;以及如何在 …...

洛谷 P3203:弹飞绵羊 ← 分块算法(单点更新、单点查询)

【题目来源】https://www.acwing.com/problem/content/2168/https://www.luogu.com.cn/problem/P3203【题目描述】 某天&#xff0c;Lostmonkey 发明了一种超级弹力装置&#xff0c;为了在他的绵羊朋友面前显摆&#xff0c;他邀请小绵羊一起玩个游戏。 游戏一开始&#xff0c;L…...

程序验证之Dafny--证明霍尔逻辑的半自动化利器

一、What is Dafny?【来自官网介绍 Dafny 】 1)介绍 Dafny 是一种支持验证的编程语言&#xff0c;配备了一个静态程序验证器。 通过将复杂的自动推理与熟悉的编程习语和工具相结合&#xff0c;使开发者能够编写可证明正确的代码&#xff08;相对于 {P}&#xff33;{Q} 这种…...

Flutter 中的 SafeArea 小部件:全面指南

Flutter 中的 SafeArea 小部件&#xff1a;全面指南 在移动应用开发中&#xff0c;处理设备屏幕的边缘是一个常见的挑战&#xff0c;尤其是考虑到现代设备通常具有不同的屏幕形状&#xff0c;如刘海屏、曲面屏等。为了确保应用内容不会覆盖这些屏幕区域&#xff0c;Flutter 提…...

webpack生成模块关系依赖图示例:查看构建产物的组成部分 依赖关系图

npm i -D webpack-bundle-analyzer core-js babel-loaderwebpack.config.js const BundleAnalyzerPlugin require(webpack-bundle-analyzer).BundleAnalyzerPlugin; module.exports {entry: ./src/index.js,output: {filename: main.js,},// mode: production, // 或者 produ…...

Spacy的安装与使用教程

官网安装指导教程 https://spacy.io/usage 安装指令 需要根据自己系统的cuda版本选择 nvcc -V pip install -U pip setuptools wheel pip install -U spacy[cuda12x] python -m spacy download zh_core_web_sm python -m spacy download en_core_web_sm...

Pathlib,一个不怕迷路的 Python 向导

大家好&#xff01;我是爱摸鱼的小鸿&#xff0c;关注我&#xff0c;收看每期的编程干货。 一个简单的库&#xff0c;也许能够开启我们的智慧之门&#xff0c; 一个普通的方法&#xff0c;也许能在危急时刻挽救我们于水深火热&#xff0c; 一个新颖的思维方式&#xff0c;也许能…...

详解绝对路径和相对路径的区别

绝对路径和相对路径是用于描述文件或目录在文件系统中位置的两种不同方式。 绝对路径&#xff08;Absolute Path&#xff09;是从文件系统的根目录开始的完整路径&#xff0c;可以唯一地确定一个文件或目录的位置。在不同的操作系统中&#xff0c;根目录的表示方式可能略有不同…...

C++二叉搜索树搜索二叉树二叉排序树

C二叉搜索树 1. 二叉搜索树的概念 二叉搜索树&#xff08;BST,Binary Search Tree)&#xff0c;也称为二叉排序树或二叉查找树。它与一般二叉树的区别在于&#xff1a;每个结点必须满足“左孩子大于自己&#xff0c;右孩子小于自己”的规则。在这种规则的约束下&#xff0c;二…...

Java 自然排序和比较器排序区别?Comparable接口和Comparator比较器区别?

注&#xff1a;如果你对排序不理解&#xff0c;请您耐心看完&#xff0c;你一定会明白的。文章通俗易懂。建议用idea运行一下案例。 1&#xff09;自然排序和比较器排序的区别&#xff1f; 自然排序是对象本身定义的排序规则&#xff0c;由对象实现 Comparable 接口&#xff…...

【CV】opencv调用DIS/LK等计算光流,前一帧和当前帧写反了有什么影响?

当在计算光流时&#xff0c;将前一帧和当前帧输入反了&#xff0c;会导致一系列问题。 在计算光流时&#xff0c;通常是将前一帧作为模板&#xff0c;根据当前帧计算光流。因为光流是描述相邻帧之间像素移动的一种方法&#xff0c;它通过比较两帧之间的像素强度或特征点的移动…...

C语言学习细节|C语言面向对象编程!函数指针如何正确使用

文章目录 1.函数指针定义2.格式3.应用回调函数动态函数调用函数的间接调用 4.结构体与函数指针结合 1.函数指针定义 函数指针就是一个指向函数的指针变量&#xff0c;与指向数据的指针不同&#xff0c;函数指针保存的是函数的地址&#xff0c;这使得程序可以动态地调用不同的函…...

C语言简要(一)

总得让她开心吧 helloworld #include <stdio.h>int main() {printf("hello world!\n");return 0; } 程序框架 #include <stdio.h> int main {return 0; }输出 printf("hello world!\n"); "里面的内容叫做“字符串”&#xff0c;prin…...

那些年我与c++的叫板(一)--string类自实现

引子&#xff1a;我们学习了c中的string类&#xff0c;那我们能不能像以前数据结构一样自己实现string类呢&#xff1f;以下是cplusplus下的string类&#xff0c;我们参考参考&#xff01; 废话不多说&#xff0c;直接代码实现&#xff1a;&#xff08;注意函数之间的复用&…...

二刷算法训练营Day08 | 字符串(1/2)

今日任务&#xff1a; 344.反转字符串 541. 反转字符串II卡码网&#xff1a;54.替换数字 151.翻转字符串里的单词卡码网&#xff1a;55.右旋转字符串 详细布置&#xff1a; 1. 344. 反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 …...

使用高防IP是应对网络安全的重要措施

使用高防IP&#xff08;High Defense IP&#xff09;在现代网络环境中显得尤为重要&#xff0c;这主要源于以下几个方面的原因&#xff1a; 一、网络安全形势严峻 随着互联网的快速发展&#xff0c;网络安全问题日益突出。各种网络攻击手段层出不穷&#xff0c;如分布式拒绝服…...

代码随想录-算法训练营day40【动态规划03:整数拆分、不同的二叉搜索树】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第九章 动态规划part03● 343.整数拆分 ● 096.不同的二叉搜索树 详细布置 今天两题都挺有难度&#xff0c;建议大家思考一下没思路&#xff0c;直接看题解&#xff0c;第一次做&#xff0c;硬想很难想出来。343. 整数…...

MySQL数据库中基本数据管理操作

使用SQL语句实现基本数据管理操作——即DML语句 1.添加数据 insert into 表名&#xff08;字段名称&#xff0c;字段名称&#xff0c;字段名称&#xff09;values&#xff08;数据&#xff0c;数据&#xff0c;数据&#xff09; 在MySQL数据库中&#xff0c;除了数字&#x…...