线程封装,互斥

文章目录

  • 线程封装
  • 线程互斥
  • 加锁、解锁
    • 认识接口
    • 解决问题
    • 理解锁

线程封装

C/C++代码混编引起的问题
在这里插入图片描述

此处pthread_create函数要求传入参数为void * func(void * )类型,按理来说ThreadRoutine满足,但是
这是在内类完成封装,所以ThreadRoutine函数实际是两个参数,第一个参数Thread* this不显示

解决方法:
第一种:写在类外,不推荐,封装性质降低
第二种:
在这里插入图片描述

首先将方法写为static方法,然后为了调用_func函数,将原本传入的参数由名称指针改为this指针
然后对this指针进行修改,用void*接受,利用static_cast<>可以更好的适应(自动变为我们需要的指针类型)
这边的t就是this,但是不能写为this,与内置冲突

多线程的创建和管理

在这里插入图片描述c++11语法的使用
在这里插入图片描述

• push_back:需要一个已经构造好的对象,并将其复制或移动到容器中。
• emplace_back:直接在容器末尾构造对象,避免了不必要的复制或移动操作

利用模版参数进行传递修改代码,保障自己需要的内容是什么
在这里插入图片描述在这里插入图片描述在这里插入图片描述

在这里插入图片描述这样就完成了一个带自定义参数的线程任务的创建执行
在这里插入图片描述

完整代码
Thread.hpp

#pragma once
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <string>
#include <functional>

using namespace std;
template<class T>
using func_t = function<void(T)>;
template<class T>
class Thread
{
public:
    Thread(const string& name, func_t<T> func, T data)
        :_tid(0), _name(name), _isrunning(false), _func(func), _data(data)
    {}
    static void* ThreadRoutine(void* args)
    {
        Thread* t = static_cast<Thread*>(args);
        t->_func(t->_data);
        exit(0);
    }
    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;
        }
        else 
        {
            return false;
        }
        
    }
    ~Thread()
    {
        cout << "~Thread()" << endl;
    }
    bool IsRunning()
    {
        return _isrunning;
    }
private:
    pthread_t _tid;
    string  _name;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

main.cc

#include <iostream>
#include "Thread.hpp"
#include <vector>

void Print(int cnt)
{
    while(cnt--)
    {
        cout << "hello world" << endl;
        sleep(1);
    }
}
string GetThreadName()
{
    static int num = 0;
    return static_cast<string>("Thread-") + to_string(++num);
}

int main()
{
    Thread<int> t(GetThreadName(), Print, 10);
    t.Start();
    t.join();

    // int num = 5; 
    // vector<Thread> threads;
    // for(int i = 0; i < num; ++i)
    // {
    //     threads.emplace_back(Print, GetThreadName());
    // }
    // for(int i = 0; i < num; ++i)
    // {
    //     cout << "Is thread is running? " << threads[i].IsRunning() << endl;
    // }

    // for(int i = 0; i < num; ++i)
    // {
    //     threads[i].Start();
    // }
    // for(int i = 0; i < num; ++i)
    // {
    //     cout << "Is thread is running? " << threads[i].IsRunning() << endl;
    // }

    // for(int i = 0; i < num; ++i)
    // {
    //     threads[i].join();
    // }



    return 0;
}

makefile文件

test_Thread:main.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f test_Thread

线程互斥

单线程抢票逻辑:
在这里插入图片描述

多线程模拟实现:
在这里插入图片描述
在这里插入图片描述

多线程会出现线程同步访问一个数据的情况,这种情况是不允许的,也不希望出现这种情况
任何时刻,只允许一个线程正在访问共享资源,这样的资源叫做临界资源

在这里插入图片描述互斥:任何时候只允许一个执行流进入临界区
访问临界资源,起到保护作用
原子性:不会被任何调度机制打断,该操作只有两态,要么完成,要么未完成
例如:

int cnt = 10;
cnt++;

这串代码会在汇编情况下变为三条语句
在这里插入图片描述在这里插入图片描述
他并不是原子的,多线程下,任何一句都可能被打断
计算机内硬件(寄存器)只有一套,但是数据是各自的私有的

多线程并发访问的时间片截止到时数据不一致问题:

在这里插入图片描述对于线程 1:
第一步 从内存将数值10写入寄存器
第二步 加加操作
在这时,他的时间片到了,那么这个时候就会进行上下文的保存

然后回进行线程2的运行:
第一步,第二步,…循环进行到cnt的值假设到了100
这时轮到线程1,线程1会进行上下文内的替换
那么这个100会被替换为11,这样这个操作就付之东流了~~

这个现象说明了,多线程并发访问cnt,不是原子的
会有数据不一致的并发访问问题

那么ticket变为负数也是这样的,这完全就是混乱的,判断也是计算
CPU有四种功能,

算:算术运算
逻:逻辑运算
中:处理内外中断
控:控制单元(时钟单元,取指令,用指令的执行)

比如上述减到负数,是因为,刚开始是并行执行逻辑判断假设ticket == 1,但是到了–操作时变成了穿行执行,假设是同时是4个执行流进入,执行流同时进行减减操作,就会减为负数的情况
这是小概率的事件,但是本质就是如此

数据在内存中是被线程共享的.
数据被读到寄存器中,本质就变成了线程的上下文,属于线程的私有数据

此时就需要加锁进行保护

加锁、解锁

认识接口

linux中进行加锁:使用pthread_mutex_init (mutex:互斥量)
在这里插入图片描述

要使用全局定义一个锁,就需要进行初始化
类型为pthread_mutex_t
初始化为PTHREAD_MUTEX_INITIALIZER
也不需要对他进行销毁,全局变量会自动进行销毁

要定义为一个局部的锁,就需要使用函数
在这里插入图片描述

局部的锁要进行销毁操作

解决问题

创建之后的加锁解锁函数
在这里插入图片描述

理解锁

加锁(以牺牲效率为代价进行解决问题):

1.给尽可能少的代码块进行加锁
2.一般加锁,都是给临界区进行加锁
执行加锁代码:

在这里插入图片描述
在这里插入图片描述
可以看到虽然正常执行,但是代码的执行速度显著下降
去掉加锁后,速度又快了不少

1.锁被创建之后,虽然它是全局变量,但是它是安全的,本身是原子性的.
2.程序员自己必须保障给临界区加锁
3.根据锁的定义,加锁只允许一个执行流进入这个临界区,所以加锁操作也只存在在一个执行流中,这时多个线程申请锁失败,失败的线程在mutex上进行阻塞,即本质进行等待
所以上述就有了,

在这里插入图片描述

pthread_mutex_trylock()的使用,成功往下走,不成功返回一个值进行程序员自行发挥
4.加锁之后,线程也可能会切换,但是其他线程处于等待状态,所以相当于当前线程抱着锁走,等他解锁才会被完全切换到其他线程(这把锁被使用期间,不能别其他任何线程使用,这也很好的保证了他的原子性)

喜欢不妨来个三连~~~

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

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

相关文章

Python | Leetcode Python题解之第174题地下城游戏

题目&#xff1a; 题解&#xff1a; class Solution:def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:n, m len(dungeon), len(dungeon[0])BIG 10**9dp [[BIG] * (m 1) for _ in range(n 1)]dp[n][m - 1] dp[n - 1][m] 1for i in range(n - 1, -1, …

Redis的实战常用一、验证码登录(解决session共享问题)(思路、意识)

一、基于session实现登录功能 第一步&#xff1a;发送验证码&#xff1a; 用户在提交手机号后&#xff0c;会校验手机号是否合法&#xff1a; 如果不合法&#xff0c;则要求用户重新输入手机号如果手机号合法&#xff0c;后台此时生成对应的验证码&#xff0c;同时将验证码进行…

C语言 | Leetcode C语言题解之第187题重复的DNA序列

题目&#xff1a; 题解&#xff1a; #define MAXSIZE 769/* 选取一个质数即可 */ typedef struct Node {char string[101];int index;struct Node *next; //保存链表表头 } List;typedef struct {List *hashHead[MAXSIZE];//定义哈希数组的大小 } MyHashMap;List * …

【百问大模型02】一文讲透RAG实战全解析

1.实时性无法更新&#xff0c;知识容易自相矛盾 2.大模型的缺点有哪些&#xff1f; 3.一个人的能力可以分为两种&#xff1a; 1&#xff09;大模型&#xff1a;推理能力&#xff0c;聪明&#xff0c;知识&#xff1b;很聪明但是缺少知识 2&#xff09;知识库&#xff1a;辅…

第一个Flask程序

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 一切准备就绪&#xff0c;现在我们开始编写第一个Flask程序&#xff0c;由于是第一个Flask程序&#xff0c;当然要从最简单的“Hello World&#xff…

打印机状态显示错误是什么原因?这5个有效方法要记好!

打印机是现代办公中不可或缺的设备之一&#xff0c;但在使用过程中&#xff0c;打印机状态显示错误是一个常见的问题。本文将详细探讨打印机状态显示错误的原因及其解决方法。 摘要 打印机状态显示错误的原因及解决方法如下&#xff1a; 1、网络连接问题&#xff1a;原因&…

【python】python基于微博互动数据的用户类型预测(随机森林与支持向量机的比较分析)(源码+数据集+课程论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

皇河将相董事长程灯虎出席第二十四届世纪大采风并获奖

仲夏时节,西子湖畔。第二十四届世纪大采风品牌人物年度盛典于6月16日至17日在杭州东方文化园隆重举行。本届盛典由亿央网、《华夏英才》电视栏目联合多家媒体共同主办,中世采文化发展集团承办,意尔康股份有限公司、宸咏集团协办,汇聚了来自全国政、商、产、学、研、媒等各界代表…

图像编辑技术的新篇章:基于扩散模型的综述

在人工智能的浪潮中&#xff0c;图像编辑技术正经历着前所未有的变革。随着数字媒体、广告、娱乐和科学研究等领域对高质量图像编辑需求的不断增长&#xff0c;传统的图像编辑方法已逐渐无法满足日益复杂的视觉内容创作需求。尤其是在AI生成内容&#xff08;AIGC&#xff09;的…

YIA主题侧边栏如何添加3D旋转标签云?

WordPress站点侧边栏默认的标签云排版很一般&#xff0c;而3D旋转标签云就比较酷炫了。下面boke112百科就以YIA主题为例&#xff0c;跟大家说一说如何将默认的标签云修改成3D旋转标签云&#xff0c;具体步骤如下&#xff1a; 1、点此下载3d标签云文件&#xff08;密码&#xf…

开源项目推荐-vue2+element+axios 个人财务管理系统

文章目录 financialmanagement项目简介项目特色项目预览卫星的实现方式&#xff1a;首次进入卫星效果的实现方式&#xff1a;卫星跟随鼠标滑动的随机效果实现方式&#xff1a;环境准备项目启动项目部署项目地址 financialmanagement 项目简介 vue2elementaxios 个人财务管理系…

java学习--集合(大写二.2)

看尚硅谷视频做的笔记 2.collection接口及方法 jdk8里有一些默认的方法&#xff0c;更多的是体现的是一种规范&#xff0c;规范更多关注的是一些抽象方法。 看接口里面的抽象方法&#xff0c;选一个具体的实现类。 测试collection的方法&#xff0c;存储一个一个数据都有哪些…

记录:[android] SSLHandshakeException: Handshake failed 问题;已解决!

1、问题描述&#xff1a;在使用Retrofit2 时在安卓老设备上&#xff08;安卓6.0&#xff09;网络无法请求、安卓 10 、 11 未出现此问题&#xff1f;what? 原因&#xff1a;服务端 TLS 版本过高 2、废话不多说、解决方案A 、添加依赖&#xff1a;implementation org.conscrypt…

安徽理工大学2计算机考研情况,招收计算机专业的学院和联培都不少!

安徽理工大学&#xff08;Anhui University of Science and Technology&#xff09;&#xff0c;位于淮南市&#xff0c;是安徽省和应急管理部共建高校&#xff0c;安徽省高等教育振兴计划“地方特色高水平大学”建设高校&#xff0c;安徽省高峰学科建设计划特别支持高校&#…

Java面试八股之myBatis与myBatis plus的对比

myBatis与myBatis plus的对比 基础与增强&#xff1a; MyBatis 是一个成熟的Java持久层框架&#xff0c;它允许开发者通过XML文件或注解来配置SQL语句和数据库映射&#xff0c;提供了一个灵活的方式来操作数据库&#xff0c;但需要手动编写所有的SQL语句和结果集映射。 MyBa…

oracle 外连接(+)和left join用法

案例1&#xff1a; select count(1) FROM TFUNDINFO A, TFUNDTYPE B WHERE A.VC_FUNDCODEB.VC_FUNDCODE() select count(1) FROM TFUNDINFO A, TFUNDTYPE B WHERE A.VC_FUNDCODEB.VC_FUNDCODE SELECT count(1): 这表示查询将返回一个计数&#xff0c;count(1)是一种常见的计数…

适用于 AI/ML 工作负载的有状态 KES

在此概念验证 &#xff08;POC&#xff09; 中&#xff0c;我们将探讨在 Kubernetes &#xff08;k8s&#xff09; 生态系统中安装和管理有状态密钥加密服务 &#xff08;KES&#xff09;。本指南促进了加密操作的无缝衔接&#xff0c;而不会将敏感的密钥材料暴露给使用型应用程…

Window和linux杀死进程的方式(命令行版)

在本文中&#xff0c;我们将探讨如何在Windows和Linux操作系统下高效地终止指定的进程&#xff0c;涵盖基本命令与高级技巧&#xff0c;确保您能灵活应对各种管理需求。 linux杀死进程 在终端中&#xff0c;我们通过下面命令找到端口运行的程序 lsof -i:72812. 然后输入下面…

见证数据的视觉奇迹——DataV Atlas

引言 前段时间一直沉迷于AI方向&#xff0c;几乎很久没碰大数据开发的相关内容了&#xff0c;今天突然看到阿里活动又推出DataV的体验了&#xff0c;我直接“啪”的一下就点进来了&#xff0c;很快啊&#xff01;本来之前开发数字孪生的时候就接触过基础的DataV操作了&#x…

北京BJ90升级新款迈巴赫大连屏四座头等舱行政四座马鞍

北京BJ90升级奔驰迈巴赫头等舱行政四座大联屏的内饰效果会非常出色&#xff0c;将为车辆带来更豪华、高端的内饰氛围。以下是升级后可能的效果&#xff1a; • 科技感提升&#xff1a;奔驰的中控系统一直以来都以其先进的科技和用户友好的界面而闻名。升级后&#xff0c;北京B…