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只是退出主线程,并不会导致进程的退出

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

svn批量解锁

问题 svn对文件进行checkout之后&#xff0c;先进行lock&#xff0c;之后再去更改&#xff0c;最后进行Commit操作&#xff1b; 上述为我们通过svn管理代码的正常方式&#xff0c;但总会有其他现象发生&#xff1b; 如果我们非正常操作&#xff0c;批量锁所有的svn文件&#x…

中国农业大学:学硕11408复试线上涨40分,今年还会持续涨吗?中国农业大学计算机考研考情分析!

中国农业大学&#xff08;China Agricultural University&#xff09;&#xff0c;简称“中国农大”&#xff0c;坐落于中国首都北京&#xff0c;由中华人民共和国教育部直属&#xff0c;中央直管副部级建制&#xff0c;水利部、农业部和北京市共建&#xff0c;位列国家“双一流…

串,数组和广义表

2.1.求next和nextval的实现 代码&#xff1a; int next_one(char *str, int len) {int result 1;if(len 1 || len 0) return len;for (size_t i 1; i < len; i){ if(compare(str, strlen-i, i)) {result i1;//break;}}return result; }int next(char *str, int *…

【校园生活小程序_超详细部署】

校园生活小程序 1 完整小程序源码2 运行环境3 初次运行3.1 启动后端程序3.1.1 导入项目&#xff0c;找到项目的pom.xml文件&#xff0c;点击ok进行打开。3.1.2 创建数据库并插入内容 3.1.3 配置项目结构信息3.1.4 配置Tomcat服务器3.1.5 正式启动后端项目3.1.6出现BUG3.1.7 解决…

小程序框架是智能融媒体平台构建的最佳线路

过去5年&#xff0c;媒体行业一直都在进行着信息化建设向融媒体平台建设的转变。一些融媒体的建设演变总结如下&#xff1a; 新闻终端的端侧内容矩阵建设&#xff0c;如App新闻端&#xff0c;社交平台上的官方媒体等新闻本地生活双旗舰客户端&#xff0c;兼顾主流媒体核心宣传…

【密评】 | 商用密码应用安全性评估从业人员考核题库(09)

Hill密码是重要古典密码之一&#xff0c;其加密的核心思想的是&#xff08;&#xff09;。 A.线性变换 B.非线性变换 C.循环移位 D.移位 著名的Kerckhoff原则是指&#xff08;&#xff09;。 A.系统的保密性不但依赖于对加密体制或算法的保密&#xff0c;而且依赖于密钥 B.系统…

深入 Go 语言:使用 math/rand 包实现高效随机数生成

深入 Go 语言&#xff1a;使用 math/rand 包实现高效随机数生成 介绍math/rand 包的核心功能设计哲学应用场景 基础使用方法初始化和种子设置设置种子创建私有随机数生成器 基础函数详解生成整数生成特定范围的整数生成浮点数随机置乱数组 进阶技巧随机数的统计属性生成正态分布…

第83天: 代码审计-PHP 项目RCE 安全调试追踪代码执行命令执行

案例一&#xff1a;CNVD拿1day-RCE命令执行-百家CMS 这里用代码审计系统搜索system&#xff0c;可以利用的是第一种 打开看细节 查找函数引用 查找$_file第一次出现的地方 这个时候就明白了&#xff0c;必须上传文件&#xff0c;然后利用文件名&#xff0c;去执行system命令 …

2024年湖北省安全员-B证证模拟考试题库及湖北省安全员-B证理论考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年湖北省安全员-B证证模拟考试题库及湖北省安全员-B证理论考试试题是由安全生产模拟考试一点通提供&#xff0c;湖北省安全员-B证证模拟考试题库是根据湖北省安全员-B证最新版教材&#xff0c;湖北省安全员-B证大…

有多少小于当前数字的数字

链接&#xff1a;https://leetcode.cn/problems/how-many-numbers-are-smaller-than-the-current-number/description/ 思路&#xff1a; 最简单的思路来说&#xff0c;就是双重for循环进行遍历&#xff0c;来判断个数&#xff0c; 优化思路&#xff0c;其中一个思路就是递推 …

vue3修改eldialog弹窗css不生效

问题&#xff1a;子组件中的eldialog没有父标签 直接使用如下是不生效的 .el-dialog{ top: 10%; } 解决&#xff1a; 加一个父标签 使用deep深度查询 .dialogClass :deep(.el-dialog) { top: 10%; } 就可以修改了

传输层协议——TCP协议

TCP协议又叫传输控制协议&#xff0c;TCP/IP协议是计算机通信网络中目前使用最多的协议&#xff0c;同时也融入了生活的方方面面&#xff0c;不管是浏览网页使用的http/https协议、物联网设备使用的MQTT/MQTTS协议与下载文件使用的ftp协议、工业以太网中使用的Modbus TCP协议等…

Elasticsearch 搜索引擎实现对文档内容进行快速检索(保姆级教程)

本文主要讲解ES如何从提取文档中提取内容&#xff08;word、pdf、txt、excel等文件类型&#xff09;&#xff0c;实现快速检索文档内容实现。 特别说明一下&#xff0c;为什么用7.10.0版本&#xff0c;因为在项目中除了精确匹配的要求&#xff0c;也会有模糊查询&#xff08;关…

k8s二进制部署--多master、负载均衡、高可用

目录 1、环境准备 1.1 服务器配置 1.2 master02 节点部署 2、负载均衡部署 2.1 下载nginx 2.2 修改nginx配置文件 2.3 启动nginx 2.3.1 检查配置文件语法 2.3.2 启动nginx服务&#xff0c;查看已监听6443端口 3. 部署keepalived服务(nginx主机&#xff0c;以nginx01为…

SOP for Oracle 23ai:Python 连接 Oracle 的两种方法

前情回顾 前文介绍了如何使用 python-oracledb 连接 Oracle 23ai 数据库&#xff0c;并演示了如何使用独立连接方式。 其中提到了支持两种连接池&#xff1a; DRCP 和 PRCP。 本文将对这两种连接池做具体演示。 DRCP 和 PRCP 连接池 连接池技术的优点不言而喻&#xff1a; 缩短…

selenium发展史

Selenium Core 2004 年&#xff0c;Thoughtworks 的工程师 Jason Huggins 正在负责一个 Web 应用的测试工作&#xff0c;由于这个项目需要频繁回归&#xff0c;这导致他不得不每天做着重复且低效的工作。为了解决这个困境&#xff0c;Jason 开发了一个运行在 JavaScript 沙箱中…

Python的for循环

for循环 Python中的for循环是一种迭代循环&#xff0c;可以迭代容器中的每一个元素。 for循环结构 示例&#xff1a; users ["汤姆", "艾米", "李华"] for i in users:print(i) 其中i为临时变量&#xff0c;仅在循环中有效&#xff1b;users…

使用可接受gitlab参数的插件配置webhook

jenkins配置 安装Generic Webhook Trigger 配置远程触发令牌 勾选Print post content和Print contributed variables用于打印值 配置gitlab 选择新增webhook 配置webhook http://JENKINS_URL/generic-webhook-trigger/invoke,将JENKINS_URL修改成自己的jenkins地址 先保存…

mysql 查询---多表设计

部分数据 1distinct去重 select distinct job from tb_emp;select * from tb_emp where id in (1,2,3); select * from tb_emp where id between 1 and 5; select * from tb_emp where name like __; #下划线匹配单个字符, %匹配任意多个字符select min(entrydate) from tb_e…

第9章.Keil5-MDK软件简介

目录 0. 《STM32单片机自学教程》专栏 9.1 主界面 9.2 文本格式编辑 9.3 代码提示&语法检测&代码模版 9.4 其他小技巧 9.4.1 TAB 键的妙用 9.4.2 快速定位函数/变量被定义的地方 9.4.3 快速注释与快速消注释 9.4.4 快速打开头文件 9.4.5 查找替换…