线程封装,互斥

2024-06-24 08:44
文章标签 互斥 线程 封装

本文主要是介绍线程封装,互斥,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

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

线程封装

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.ccg++ -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.chinasem.cn/article/1089660

相关文章

公共筛选组件(二次封装antd)支持代码提示

如果项目是基于antd组件库为基础搭建,可使用此公共筛选组件 使用到的库 npm i antdnpm i lodash-esnpm i @types/lodash-es -D /components/CommonSearch index.tsx import React from 'react';import { Button, Card, Form } from 'antd'

React+TS前台项目实战(十七)-- 全局常用组件Dropdown封装

文章目录 前言Dropdown组件1. 功能分析2. 代码+详细注释3. 使用方式4. 效果展示 总结 前言 今天这篇主要讲全局Dropdown组件封装,可根据UI设计师要求自定义修改。 Dropdown组件 1. 功能分析 (1)通过position属性,可以控制下拉选项的位置 (2)通过传入width属性, 可以自定义下拉选项的宽度 (3)通过传入classN

axios全局封装AbortController取消重复请求

为什么? 问题:为什么axios要配置AbortController?防抖节流不行吗? 分析: 防抖节流本质上是用延时器来操作请求的。防抖是判断延时器是否存在,如果存在,清除延时器,重新开启一个延时器,只执行最后一次请求。节流呢,是判断延时器是否存在,如果存在,直接return掉,直到执行完这个延时器。事实上,这些体验感都不算友好,因为对于用户来说,得等一些时间,尤其是首次请求,不是那么流畅

Python利用qq邮箱发送通知邮件(已封装成model)

因为经常喜欢写一些脚本、爬虫之类的东西,有需要通知的时候,总是苦于没有太好的通知方式,虽然邮件相对于微信、短信来说,接收性差了一些,但毕竟免费,而且支持html直接渲染,所以,折腾了一个可以直接使用的sendemail模块。这里主要应用的是QQ发邮件,微信关注QQ邮箱后,也可以实时的接收到消息,肾好! 好了,废话不多说,直接上代码。 # encoding: utf-8import lo

Java线程面试题(50)

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题。Java语言一个重要的特点就是内置了对并发的支持,让Java大受企业和程序员的欢迎。大多数待遇丰厚的Java开发职位都要求开发者精通多线程技术并且有丰富的Java程序开发、调试、优化经验,所以线程相关的问题在面试中经常会被提到。 在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程,

线程池ThreadPoolExecutor类源码分析

Java并发编程:线程池的使用   在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:   如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。   那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

线程Lock

线程Lock   在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问。本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock。   也许有朋友会问,既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?这个问题将在下面进行阐述。本文先从s

示例:推荐一个基于第三方开源控件库DataGridFilter封装的FilterColumnDataGrid,可以像Excel拥有列头筛选器

一、目的:基于第三方开源控件库DataGridFilter封装的FilterColumnDataGrid,可以像Excel拥有列头筛选器,感兴趣的可以去下方链接地址查看开源控件库地址。本控件封装的目的在于将第三方库的皮肤和样式封装到皮肤库中可统一设置样式,同时生成nuget方便调用 二、效果如下 三、环境 VS2022 Net7 四、使用方式 1、安装nuget包:H.Con

Linux-笔记 线程同步机制

目录 前言 实现 信号量(Semaphore) 计数型信号量 二值信号量  信号量的原语操作 无名信号量的操作函数 例子 互斥锁(mutex) 互斥锁的操作函数 例子 自旋锁 (Spinlock) 自旋锁与互斥锁的区别 自旋锁的操作函数 例子 前言         线程同步是为了对共享资源的访问进行保护,确保数据的一致性,由于进程中会有多个线程的存在,

jmeter之Thread Group(线程组)

Thread Group(线程组) 1.线程组,或者可以叫用户组,进行性能测试时的用户资源池。 2.是任何一个测试计划执行的开始点。 3.上一篇提到的“控制器”和“HTTP请求”(采集器)必须在线程组内;监听器等其他组件,可以直接放在测试计划下。 线程组设置参数的意义 我们以下图为例,进行详细说明。见下图:  区域1(在取样器错误后要执行的动作) 这个区域的主要作用很明显,在线程内