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

个人网站建设模板下载百度推广的四种收费形式

个人网站建设模板下载,百度推广的四种收费形式,房地产网络营销方式,代理网页游戏需要多少钱📝前言: 这篇文章我们来讲讲Linux——基于单例模式的线程池设计 🎬个人简介:努力学习ing 📋个人专栏:Linux 🎀CSDN主页 愚润求学 🌄其他专栏:C学习笔记,C语言…

📝前言:

这篇文章我们来讲讲Linux——基于单例模式的线程池设计

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏


这里写目录标题

  • 一,设计框架
    • 1. 总体框架
    • 2. 线程池模块
      • 2.1 什么是线程池
      • 2.2 设计思路
    • 3. 什么是单例模式
      • 3.1 饿汉模式
      • 3.2 懒汉模式(更推荐)
  • 二,实现代码
  • 三,测试结果
  • 四,线程安全和重入问题
  • 五、锁的其他问题
    • 1. 死锁问题
    • 2. 死锁四个必要条件
    • 3. 避免死锁
  • 六、STL和智能指针的线程安全

一,设计框架

1. 总体框架

我们需要的模块

  • 日志Mylog:用于DEBUG
  • 线程MyThread:用于生成多个线程
  • 线程池ThreadPool,利用前两个模块来实现一个线程池

2. 线程池模块

2.1 什么是线程池

线程池 是一种多线程处理模式,属于池化技术的一种。它通过提前创建并管理一定数量的线程,重复利用这些线程处理任务,避免了频繁创建和销毁线程的开销,从而提升系统性能和资源利用率

2.2 设计思路

  • 我们用一个queue来存储待处理的任务
  • 用一个vector来存储线程

3. 什么是单例模式

某些类, 只应该具有⼀个对象(实例), 就称之为单例

  • 通常把构造函数,拷贝,赋值…设置成私有,或者直接禁用(在外部无法创建实例)
  • 只保留一个static实例成员,这样类在被加载的时候,这个实例就存在了。(static成员不属于任何一个对象,而属于类,只有一份)
    • 注意static成员的初始化是在类外的(但是属于类的一部分,可以访问类的私有成员)
  • 然后提供一个public的接口来返回这个实例给外部使用

3.1 饿汉模式

  • 加载类的时候,直接创建整个static实例成员
  • 问题:如果静态实例的体积大,则加载时间长

3.2 懒汉模式(更推荐)

  • 延迟加载,我只定义静态成员实例的指针。
  • 当真正使用到这个成员时,再创建并赋值给指针(这样可以提高内存使用率)
    • 即:在GetInstance时发现指针为空再创建

可是还有几个设计上的问题:

  • 没有实例,我们怎么调GetInstance创造实例?
    • 所以GetInstance也是Static
  • 获取实例不是线程安全的,要加锁(不然可能创建多个实例)
    • 这个锁是什么锁?也需要是static的锁,因为没实例的时候拿不到类的锁。

二,实现代码

细节我就不讲了,感兴趣的可以看注释

#pragma once
#include "MyThread.hpp"
#include "Mylog.hpp"
#include <vector>
#include <queue>
#include "Task.hpp"
#include <mutex>
#include <condition_variable>// 先不考虑单例模式
using namespace std;
using namespace tr;template <typename T> // 任务类型
class ThreadPool
{
private:ThreadPool(int th_num = 3): _th_num(th_num),_isrunning(false),_wait_thread(0){}void HandlerTask() // 单个线程处理任务的方法{while (true){LOG(LogLevel::DEBUG) << GetThreadName() << " is running";_mutex.lock();while (_tasks_queue.empty() && _isrunning) // 条件变量一定要注意虚假唤醒{_wait_thread++;// LOG(LogLevel::DEBUG) << GetThreadName() << " is waiting";_cond.wait(_mutex);// LOG(LogLevel::DEBUG) << GetThreadName() << " is waked";_wait_thread--; // 如果等待被唤醒(唤醒是由别的地方唤醒的)}// 如果还有任务就先不退if (_tasks_queue.empty() && !_isrunning){_mutex.unlock(); // 尽早释放锁LOG(LogLevel::DEBUG) << GetThreadName() << " quit";break;}T t = _tasks_queue.front();_tasks_queue.pop();_mutex.unlock(); // 尽早释放锁LOG(LogLevel::DEBUG) << GetThreadName() << " 获得一个任务...";t(); // 处理任务}}void Start() // 启动线程池{if (!_isrunning){LOG(LogLevel::DEBUG) << "启动线程池...";_isrunning = true;// 必须要先开空间(因为扩容不是线程安全的)// 1. 创建 Mythread 对象// 2. 启动线程(线程开始执行HandlerTask)// 3. vector扩容(移动现有线程对象,原来的迭代器失效)// 如果 3. 的时候有线程在执行 HandlerTask,则原 this 指针会失效(导致段错误)// 以上为个人理解_threads.reserve(_th_num);// lock_guard<mutex> lock(_mutex); 不能这样做,又是为什么呢???for (int i = 0; i < _th_num; i++){// 用 HandlerTask(需要捕捉 this指针) 构造 Mythread 临时对象(隐式类型转换),_threads.emplace_back([this](){HandlerTask();});}}elseLOG(LogLevel::DEBUG) << "线程池已经启动, 请勿重复启动...";}// 禁用拷贝和赋值ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(const ThreadPool<T> &) = delete;public:static ThreadPool<T> *GetInstance(){if (_instance == nullptr){_lock.lock();if (_instance == nullptr) // 双层 if 判断,只有一个线程能拿到锁进入这里,不会全部线程都通过这个判断{_instance = new ThreadPool<T>();_instance->Start(); // 启动线程池LOG(LogLevel::DEBUG) << " 创建线程池单例" ;return _instance;}}LOG(LogLevel::DEBUG) << " 获取线程池单例";return _instance;}void Stop() // 暂停(结束)线程池{lock_guard<mutex> lock(_mutex);if (!_isrunning)return;_isrunning = false;// 唤醒所有线程,让线程知道 _isrunning 已经为 false 了_cond.notify_all();LOG(LogLevel::INFO) << "线程池退出...";}void Wait() // 等待所有线程{for (auto &th : _threads){// LOG(LogLevel::INFO) << th.GetName() << " 退出...";th.Join();}}void Enqueue(const T &task) // 入队任务{lock_guard<mutex> lock(_mutex);if (_isrunning){_tasks_queue.push(task);// LOG(LogLevel::INFO) << "插入了一个任务";if (_wait_thread > 0)_cond.notify_one();}}~ThreadPool(){}private:bool _isrunning;queue<T> _tasks_queue;vector<Mythread> _threads;int _th_num; // 线程数量int _wait_thread;mutex _mutex; // 标准库的锁不用初始化condition_variable_any _cond;static ThreadPool<T> *_instance; // 静态成员实例static mutex _lock;
};template <typename T>                              // 模板类静态成员初始化必须带 template <typename T>, 因为模版要实例化
ThreadPool<T> *ThreadPool<T>::_instance = nullptr; // 还要声明模版的作用域
template <typename T>
mutex ThreadPool<T>::_lock;

主体代码为上面这部分的,如果想要获取其中其他模块的代码,可以访问我的GIthub

三,测试结果

在这里插入图片描述
在这里插入图片描述

四,线程安全和重入问题

线程安全(描述线程):就是多个线程在访问共享资源时,能够正确地执行,不会相互干扰或破坏彼此的执行结果。一般而言,多个线程并发同一段只有局部变量的代码时,不会出现不同的结果。但是对全局变量或者静态变量执行操作,并且没有锁保护的情况下,容易出现该问题

重入(描述函数):同一个函数被不同的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进⼊,我们称之为重⼊。一个函数在重⼊的情况下,运行结果不会出现任何不同或者任何问题,则该函数被称为可重入函数,否则,是不可重入函数

重入分两种:

  • 多线程重入函数
  • 信号导致⼀个执行流重复进入函数

线程安全不一定是可重入的,而可重入函数则⼀定是线程安全的

五、锁的其他问题

1. 死锁问题

死锁是指在⼀组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站⽤不会释放的资源⽽处于的⼀种永久等待状态。

  • 比如:一个公共资源的访问需要两把锁,但是现在线程 A 和线程 B各自占一个,并且都不愿意放,还不断申请对方的,导致两者都申请不到造成死锁
    在这里插入图片描述
  • 还比如,单线程死锁(自己带着锁,还自己把自己挂起了)

在这里插入图片描述

2. 死锁四个必要条件

  • 互斥条件:一个资源每次只能被一个执行流使用
  • 请求与保持条件:一个执行流因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:一个执行流已获得的资源,在未使用完之前,不能强行剥夺
  • 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系

3. 避免死锁

那就是破坏死锁的四个必要条件,使之不满足…
有避免死锁的算法:银行家算法、死锁检测算法(这里不做介绍)

六、STL和智能指针的线程安全

  • STL 容器,不是线程安全的,比如扩容等操作,是可能被切换的
  • 智能指针本身是线程安全的,但是它们所指向的资源不是线程安全的

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

http://www.dtcms.com/wzjs/279008.html

相关文章:

  • 做app好 还是讯网站好seo是什么专业的课程
  • 自己做网站出证书镇江交叉口优化
  • 青岛城乡建设部网站首页网片
  • wordpress仿b站网站运营管理
  • 网站每年维护费用模板网站建设
  • 有了网站域名如何做网站河南网站建设定制
  • 大神做的动漫网站梁水才seo优化专家
  • wordpress 256m内存小学生班级优化大师
  • 唐山seo设计网站百度电脑版下载官方
  • java做音乐网站百度联盟项目看广告挣钱
  • 新手做网站做那个百搜科技
  • 青岛 网站制作公司湖南今日新闻最新头条
  • 做视频的素材什么网站好刷神马网站优化排名
  • 手机网站首页模板seo什么意思中文意思
  • 网站如何为关键词做外链黄金网站软件免费
  • 做茶叶网站的目的和规划杭州网站推广优化
  • 成都协会网站建设网络推广app是违法的吗
  • 临海最火自适应网站建设培训机构排名全国十大教育机构排名
  • 做网站找个人还是找公司企业网络营销推广平台
  • 焦作网站建设哪家正规深圳网络推广的公司
  • 做电台需要的文章从哪个网站找佛山网站建设制作公司
  • 全能网站服务器对网络营销的认识
  • 网页设计全部代码优化大师人工服务电话
  • 峰峰专业做网站建立一个国外的网站
  • 西宁建网站需要多少钱全国各城市疫情高峰感染进度
  • 网站建设项目采购公告免费网络推广工具
  • 深圳家居网站建设公司上海优化seo排名
  • 梧州网站建设360开户推广
  • 建网站公司浩森宇特电商怎么做?如何从零开始学做电商赚钱
  • 网站优化流程百度收录网址提交