Linux -- 单例模式

news/2025/1/7 23:03:03/

目录

概念

主要特点

饿汉模式和懒汉模式

饿汉模式

懒汉模式

应用场景 

单例模式下的线程池

GetInstance 函数:

单例模式禁止赋值和拷贝: 

完整代码:


概念

单例模式是软件工程中的一种设计模式,属于创建型模式。它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。

主要特点

  1. 唯一实例:整个应用程序能存在该类的一个实例
  2. 私有构造函数:防止其他对象使用new关键字直接实例化。
  3. 静态工厂方法:提供一个静态方法作为创建实例的全局访问点。
  4. 延迟初始化:有些实现方式会直到第一次使用时才创建实例,以节省资源。

单例模式的类禁止赋值或拷贝,否则破坏单例原则!!

饿汉模式和懒汉模式

饿汉模式和懒汉模式是实现单例模式的两种常见策略,它们主要区别在于何时创建单例实例。

饿汉模式

饿汉模式是在类加载时就立即创建单例对象。这种方式简单且线程安全,因为对象在类加载时就已经被创建,所以在多线程环境下不会出现两个线程同时创建对象的情况。缺点是如果这个对象在整个应用程序运行期间都不被使用,那么将造成资源浪费。

template <typename T>
class Singleton {static T data;
public:static T* GetInstance() {return &data;}
};

懒汉模式

懒汉模式是延迟到真正需要该对象的时候才创建它。懒汉模式最核心的思想是 “ 延时加载 ”,从而能够优化服务器的启动速度。这种方式可以节省内存资源,因为它只会在第一次调用 getInstance 方法时创建对象。然而,这种实现方式不是线程安全的,因为在多线程环境中可能会导致多个实例被创建。为了保证线程安全,可以在 getInstance 方法中加入同步机制,但这样又会影响性能。在类中存在一个指针,可执行程序加载到内存时,只是初始化指针,并不会创建具体的对象,第一次调用getInstance 方法时才会 new 对象。

template <typename T>
class Singleton {static T* inst;//指针
public:static T* GetInstance() {if (inst == NULL) //第一次调用{inst = new T();} return inst;}
};

应用场景 

  • 如果你确定你的单例对象一定会被使用,或者它的初始化成本很小,那么可以选择饿汉模式;
  • 如果你希望延迟初始化以节省资源,并且能够妥善处理线程安全问题,那么懒汉模式可能更适合你。

单例模式下的线程池

GetInstance 函数:

双重判定空指针,可以避免不必要的锁竞争,提高性能。

static ThreadPool<T> *GetInstance()//静态函数{// 如果是第一个调用,则会创建单例if (_instance == nullptr){//不在if外面加锁,是因为如果是第二次调用的话,不需要创建单例,不需要保护资源//只需要获取单例,所以只需要在第一次调用的时候加锁,既保证线程安全又降低加锁成本LockGuard lockguard(&_lock);//第二次判断,防止在阻塞等待锁的过程中已经由别的线程创建单例了//但由于本线程已经通过if判断了,导致再创建了一次实例if (_instance == nullptr){_instance = new ThreadPool<T>();_instance->InitThreadPool();_instance->Start();LOG(DEBUG, "创建线程池单例成功");}}// 不是第一次调用,则直接获取单例,不需要重新创建else{LOG(DEBUG, "获取线程池单例成功");}return _instance;}

单例模式禁止赋值和拷贝: 

// 禁用赋值和拷贝
ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
ThreadPool(const ThreadPool<T> &) = delete;

 

完整代码:

#pragma once// 线程池的封装#include <pthread.h>
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include "Thread.hpp"
#include "LockGuard.hpp"
#include "Log.hpp"
using namespace ThreadModule;const static int gdefaultthreadnum = 10;
template <typename T>
class ThreadPool
{
private:void LockQueue(){pthread_mutex_lock(&_mutex);}void UnlockQueue(){pthread_mutex_unlock(&_mutex);}void ThreadSleep() // 谁调用谁休眠{pthread_cond_wait(&_cond, &_mutex);}void ThreadWakeUp() // 唤醒一个线程{pthread_cond_signal(&_cond);}void ThreadWakeUpAll() // 唤醒全部线程{pthread_cond_broadcast(&_cond);}void HandlerTask(std::string name) // 第一个参数是this指针{while (true) // 一直处理任务,直到线程池退出且任务队列为空{LockQueue(); // 任务队列是临界资源,需要保护// 任务队列为空,且线程池还在运行,则线程休眠while (_task_queue.empty() && _isrunning){_waitnum++;ThreadSleep();_waitnum--;}// 任务队列为空,且线程池不运行了,退出if (_task_queue.empty() && !_isrunning){UnlockQueue(); // 解锁break;         // 退出}// 还有任务没处理,则处理任务T t = _task_queue.front(); // 取出任务_task_queue.pop();UnlockQueue(); // 线程已经取出任务,任务已为线程私有,且任务可能比较耗时,解锁LOG(DEBUG, "%s get a task", name.c_str());t();LOG(DEBUG, "%s handler a task,result is: %s", name.c_str(), t.ResultToString().c_str());}}// 单例模式下构造函数是私有的ThreadPool(int num = gdefaultthreadnum): _threadnum(num), _waitnum(0), _isrunning(false){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);LOG(INFO, "ThreadPool Construct");}// 只是传了线程需要的参数,线程还没有创建出来void InitThreadPool(){for (int i = 0; i < _threadnum; i++){std::string name = "thread-" + std::to_string(i + 1);// 由于 HandlerTask 的第一个参数是 this 指针,第二个参数是 string 类型// 而 Thread.hpp 中 Thread 类的构造函数要求传的函数的参数只能有 string 类型// 可以用 bind 函数对 HandlerTask 的第一个参数进行绑定(绑了 this 指针)// (_1 就是绑定第一个参数,_2 就是绑定第二个参数)// 绑定之后,使用 HandlerTask 就只需要传 string 类型的参数// 相当于函数的参数从( this,string )变成了( string )_threads.emplace_back(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1), name);LOG(INFO, "init thread %s done", name.c_str());}_isrunning = true;}// 创建线程void Start(){for (auto &thread : _threads){thread.Start();}}// 禁用赋值和拷贝ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(const ThreadPool<T> &) = delete;public:static ThreadPool<T> *GetInstance(){// 如果是第一个调用,则会创建单例if (_instance == nullptr){//不在if外面加锁,是因为如果是第二次调用的话,不需要创建单例,不需要保护资源//只需要获取单例,所以只需要在第一次调用的时候加锁,既保证线程安全又降低加锁成本LockGuard lockguard(&_lock);//第二次判断,防止在阻塞等待锁的过程中已经由别的线程创建单例了//但由于本线程已经通过if判断了,所以再创建了一次if (_instance == nullptr){_instance = new ThreadPool<T>();_instance->InitThreadPool();_instance->Start();LOG(DEBUG, "创建线程池单例成功");}}// 不是第一次调用,则直接获取单例,不需要重新创建else{LOG(DEBUG, "获取线程池单例成功");}return _instance;}void Stop(){LockQueue(); // 状态值也是临界资源,需要保护_isrunning = false;// 线程池不运行了,需要唤醒所以在条件变量下等待的线程// 否则线程会一直阻塞等待条件变量,无法被 joinThreadWakeUpAll();UnlockQueue();}void Wait(){for (auto &thread : _threads){thread.Join();LOG(INFO, "%s is quit...", thread.name().c_str());}}bool Enqueue(const T &t){bool ret = false;LockQueue();// 线程池在运行中,才可以放入任务if (_isrunning){_task_queue.push(t);// 有线程在等任务,唤醒线程if (_waitnum > 0)ThreadWakeUp();LOG(DEBUG, "enqueue task success");ret = true; // 任务插入成功}UnlockQueue();return ret;}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}private:int _threadnum; // 线程的个数std::vector<Thread> _threads; // 管理线程std::queue<T> _task_queue;    // 任务队列pthread_mutex_t _mutex; // 互斥锁pthread_cond_t _cond;   // 信号量bool _isrunning; // 线程池的启动状态int _waitnum;    // 线程等待的个数// 添加单例模式static ThreadPool<T> *_instance;static pthread_mutex_t _lock;
};// 初始化
template <typename T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;template <typename T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;


http://www.ppmy.cn/news/1560990.html

相关文章

基于氢氧燃料电池的分布式三相电力系统Simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于氢氧燃料电池的分布式三相电力系统Simulink建模与仿真&#xff0c;仿真输出燃料电池中氢氧元素含量变化以及生成的H2O变化情况。 2.系统仿真结果 3.核心程序与模型 版本…

Cursor连接腾讯云Cloud Studio开发环境

文章目录 环境准备Cloud StudioCursor Cursor连接Cloud Studio开发环境 环境 腾讯云Cloud Studio语言模板All In One实例Windows 11Firefox 133.0.3 (64 位)Cursor 0.44.9 准备 Cloud Studio 在腾讯云Cloud Studio&#xff08; https://ide.cloud.tencent.com/ &#xff09…

JAVA:Spring Boot 集成 Quartz 实现分布式任务的技术指南

1、简述 Quartz 是一个强大的任务调度框架&#xff0c;允许开发者在应用程序中定义和执行定时任务。在 Spring Boot 中集成 Quartz&#xff0c;可以轻松实现任务的调度、管理、暂停和恢复等功能。在分布式系统中&#xff0c;Quartz 也支持集群化的任务调度&#xff0c;确保任务…

计算机毕业设计Python电商品推荐系统 商品比价系统 电商比价系统 商品可视化 商品爬虫 机器学习 深度学习 京东爬虫 国美爬虫 淘宝爬虫 大数据

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

使用python将多个Excel表合并成一个表

import pandas as pd# 定义要合并的Excel文件路径和名称 file_paths [file1.xlsx, file2.xlsx, file3.xlsx, file4.xlsx, file5.xlsx]# 创建一个空的DataFrame来存储合并后的数据 merged_data pd.DataFrame()# 循环遍历每个Excel文件&#xff0c;并读取其中的数据 for file_p…

HTML基础入门:结构、文本格式化与多媒体图片

HTML基础入门&#xff1a;结构、文本格式化与多媒体图片 HTML&#xff08;超文本标记语言&#xff09;是构建网页的基础语言&#xff0c;是每一位前端开发者的必修课。 HTML文档基本结构 HTML文档的组成 HTML文档的结构就像建筑的框架&#xff0c;支撑起整个网页内容。一个标…

vulnhub——Earth靶机

使用命令在kali查看靶机ip arp-scan -l 第一 信息收集 使用 nmap 进行 dns 解析 把这两条解析添加到hosts文件中去&#xff0c;这样我们才可以访问页面 这样网站就可以正常打开 扫描ip时候我们发现443是打开的&#xff0c;扫描第二个dns解析的443端口能扫描出来一个 txt 文件…

多模态大模型文生图和图生文的主要技术

1 图生文 CLIP 该模型架构由图像编码器和文本编码器组成。图像编码器将图像转换为嵌入&#xff08;数字列表&#xff09;&#xff0c;文本编码器将文本转换为嵌入。 这两个编码器在成批的图像-文本对上进行训练&#xff0c;其中文本描述图像。编码器的训练方式如下&#xff1…