线程封装,互斥

news2024/12/27 15:45:35

文章目录

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

线程封装

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.加锁之后,线程也可能会切换,但是其他线程处于等待状态,所以相当于当前线程抱着锁走,等他解锁才会被完全切换到其他线程(这把锁被使用期间,不能别其他任何线程使用,这也很好的保证了他的原子性)

喜欢不妨来个三连~~~

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

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

相关文章

Python 围棋

效果图 完整代码 源码地址&#xff1a;Python 围棋 # 使用Python内置GUI模块tkinter from tkinter import * # ttk覆盖tkinter部分对象&#xff0c;ttk对tkinter进行了优化 from tkinter.ttk import * # 深拷贝时需要用到copy模块 import copy import tkinter.me…

高纯PFA容量瓶PFA试剂瓶在半导体材料的应用

在半导体生产过程中&#xff0c;为避免金属污染对硅器件性能造成不利影响&#xff0c;碳化硅产业链不同阶段产品&#xff08;如衬底、外延、芯片、器件&#xff09;表面的痕量杂质元素浓度表征至关重要。 在实验人员使用质谱法高精度检测第三代半导体碳化硅材料的痕量杂质浓度…

Linux - 探秘 Linux 的 /proc/sys/vm 常见核心配置

文章目录 PreLinux 的 /proc/sys/vm 简述什么是 /proc/sys/vm&#xff1f;主要的配置文件及其用途参数调整对系统的影响dirty_background_ratio 和 dirty_ratioswappinessovercommit_memory 和 overcommit_ratiomin_free_kbytes 实例与使用建议调整 swappiness设置 min_free_kb…

2024.6.23刷题记录

目录 一、P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 1.hash表-一次遍历 2.双指针&#xff08;同向&#xff0c;可以算滑动窗口&#xff09;-排序 二、P8667 [蓝桥杯 2018 省 B] 递增三元组 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 1.排序指针 2…

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

题目&#xff1a; 题解&#xff1a; class Solution {const int L 10;unordered_map<char, int> bin {{A, 0}, {C, 1}, {G, 2}, {T, 3}}; public:vector<string> findRepeatedDnaSequences(string s) {vector<string> ans;int n s.length();if (n < L…

《AI旋律:创意产业的重塑与共生》

AI乐章&#xff1a;技术革命下的创意产业新生态 在数字化浪潮的推动下&#xff0c;音乐创作领域迎来了前所未有的变革——AI音乐大模型的横空出世&#xff0c;犹如一颗石子投入平静的湖面&#xff0c;激起了层层涟漪。这些模型以令人难以置信的速度和多样性&#xff0c;将音乐…

WinForm 2048

WinForm 2048 是一个基于 Windows 窗体应用程序&#xff08;WinForms&#xff09;实现的经典益智游戏——2048。这个游戏通过简单的滑动或点击操作&#xff0c;将相同数字的方块合并&#xff0c;以生成更大的数字方块&#xff0c;最终目标是创造出一个数字为 2048 的方块。 游…

C++ | Leetcode C++题解之第188题买卖股票的最佳时机IV

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxProfit(int k, vector<int>& prices) {if (prices.empty()) {return 0;}int n prices.size();k min(k, n / 2);vector<int> buy(k 1);vector<int> sell(k 1);buy[0] -prices[0]…

Ubuntu 之Glade图形化设计器

演示环境说明&#xff1a;本机使用Windows 11 家庭版本搭载 Ubuntu 22.04.4 LTS 子系统&#xff0c;同时并安装Ubuntu桌面虚拟化软件XLaunch。 如果没有搭建好上述问题&#xff0c;请参考&#xff1a;windows11子系统Ubuntu 22.04.4子安装图形化界面 Glade是什么&#xff1f;…

驾校预约管理系统

摘 要 随着驾驶技术的普及和交通安全意识的增强&#xff0c;越来越多的人选择参加驾校培训&#xff0c;以获取驾驶执照。然而&#xff0c;驾校管理面临着日益增长的学员数量和繁琐的预约管理工作。为了提高驾校的管理效率和服务质量&#xff0c;驾校预约管理系统成为了必不可少…

[spring] Spring MVC - CRUD 操作

[spring] Spring MVC - CRUD 操作 基础实现源自于这两篇笔记&#xff1a; [spring] Spring Boot REST API - 项目实现[spring] Spring Boot REST API - CRUD 操作 除了 Rest API 部分改成了 Controller 之外&#xff0c;其他没什么变化&#xff0c;还是使用 service --> …

6/23 第四周 python操作excel

excel对于python来说就是一个二维数组&#xff0c;只是多了一个多sheet的index的索引&#xff0c;在确定索引之后&#xff0c;不管是读取还是写入&#xff0c;都是类似于二维数组。对于数据的处理&#xff0c;读取和写入就够了&#xff0c;如果要应用图表&#xff0c;个人觉得还…

nest.js关键笔记

Nest.js 介绍核心功能设计模式&#xff1a;IOC 控制反转 DI 依赖注入前置知识&#xff1a;装饰器前置知识装饰器-实现一个GET请求 Nestjs脚手架Nestjs cli 常用命令 RESTful 风格设计Nestjs 控制器控制器中常见的参数装饰器 Session 实例Nestjs 提供者**工厂模式**异步模式 Nes…

Linux 7种 进程间通信方式

传统进程间通信 通过文件实现进程间通信 必须人为保证先后顺序 A--->硬盘---> B&#xff08;B不知道A什么时候把内容传到硬盘中&#xff09; 1.无名管道 2.有名管道 3.信号 IPC进程间通信 4.消息队列 5.共享内存 6.信号灯集 7.socket通信 一、无名管道&a…

《书生·浦语大模型实战营》第5课 学习笔记:LMDeploy 量化部署 LLM 实践

文章大纲 0.背景知识与简介计算机组成原理&#xff1a;变量的存储参数量与推理的关系 1.LMDeploy环境部署1.1 创建开发机1.2 创建conda环境InternStudio开发机创建conda环境&#xff08;推荐&#xff09;本地环境创建conda环境 1.3 安装LMDeploy 2.LMDeploy模型对话(chat)2.1 H…

Golang | Leetcode Golang题解之第187题重复的DNA序列

题目&#xff1a; 题解&#xff1a; const L 10 var bin map[byte]int{A: 0, C: 1, G: 2, T: 3}func findRepeatedDnaSequences(s string) (ans []string) {n : len(s)if n < L {return}x : 0for _, ch : range s[:L-1] {x x<<2 | bin[byte(ch)]}cnt : map[int]in…

Python | Leetcode Python题解之第187题重复的DNA序列

题目&#xff1a; 题解&#xff1a; L 10 bin {A: 0, C: 1, G: 2, T: 3}class Solution:def findRepeatedDnaSequences(self, s: str) -> List[str]:n len(s)if n < L:return []ans []x 0for ch in s[:L - 1]:x (x << 2) | bin[ch]cnt defaultdict(int)for…

期货交易纪律2024年6月22号

文章目录 期货交易系统构建第一步、选品第二步、开仓纪律第三步、持仓 2024年6月22号&#xff0c;开始写期货交易的第三篇日记。 交易记录&#xff1a;市场继续震荡&#xff0c;这两天无交易&#xff0c;继续梳理一些期货交易选品&#xff0c;周末详细的了解了一下豆粕&#xf…

axure制作菜单下拉、隐藏、点击选中效果

在高保真原型中&#xff0c;制作导航栏菜单时&#xff0c;需要达到点击下拉按钮&#xff0c;子菜单自动弹出&#xff0c;点击其中一个子菜单项可栏目变为选中状态且跳转到对应的子页面。制作材料可以从antdesign中去下载&#xff0c;以下述网络配置菜单为例。在箭头处添加互动效…

Pytest和Unitest框架对比

在学到自动化的时候,很多同学都遇到了Pytest和Unitest框架,有的人是两个都学,但是学的不精只是知道分别怎么用.不了解两个区别是什么.有的是犹豫到底要学习那个框架.其实要做好自动化测试,是有必要了解不同框架之间的差异化的. Pytest 特点: Pytest采用了更简洁、更灵活的语法…