单例模式

概念介绍

在面向对象编程中,我们通常借助构造函数快速创建对象实例,这是对象实例化的常规方式。但在实际开发中,若某个对象包含海量数据(例如占用几十甚至上百 GB 的内存空间),如果频繁通过构造函数创建该对象的多个实例,会导致内存占用急剧膨胀,不仅会让程序体积异常庞大,还可能引发内存溢出、性能大幅下降等严重问题。

单例模式(Singleton Pattern)正是为解决这类问题而生的经典设计模式:它通过将类的构造函数私有化,同时显式禁用拷贝构造函数和赋值运算符(C++ 中),从语法层面杜绝外部随意创建类的实例;并通过类内部提供的全局访问接口,确保整个程序的生命周期内,该类仅存在唯一的一个实例对象。这既避免了重复创建大体积对象带来的内存浪费,也保证了全局范围内对该唯一实例的统一访问,有效提升程序的资源利用率和运行稳定性。

两种使用方式

饿汉方式

假设有一个壮汉,饿汉式的创建方式就好比这个壮汉刚吃完饭,不等后续使用,就立刻把碗洗干净并放好 —— 对应到单例模式的实现逻辑中,就是程序一启动(进程初始化阶段),不管后续是否会用到这个单例对象,都会立即完成实例的初始化和创建

这种 “提前创建” 的特性,虽然能保证实例的线程安全性(无需额外加锁)、获取实例时的效率极高,但也存在明显的弊端:如果这个单例对象占用的内存资源较多(比如包含大体积数据),且程序运行全程都没有实际使用该实例,就会造成内存资源的无意义浪费。因此在实际开发中,饿汉式单例的使用场景相对有限,更多用于实例体积小、必定会被使用的场景。

demo演示

#include <iostream>
#include <string>
using namespace std;
class Singleton
{
private:
    Singleton()
    {
    }
    // 显式禁用拷贝构造接口
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;
    static Singleton instance;

public:
    // 实例获取接口
    static Singleton &getInstance()
    {
        return instance;
    }
};
// 关键步骤,类外定义程序加载直接进行初始化
Singleton Singleton::instance;

// 主函数测试
int main()
{
    Singleton &obj1 = Singleton::getInstance();
}

懒汉方式

懒汉式单例模式与饿汉式的核心差异,恰好体现在你比喻的 “洗碗时间” 上:如果说饿汉式是 “吃完饭立马洗碗”,那懒汉式就是 “吃完饭不着急洗碗,非要等到下一顿要用餐、需要用到干净碗的时候,才去洗碗”。

对应到技术实现上,懒汉式单例不会在程序启动 / 加载阶段提前创建实例,而是将实例的初始化操作完全 “延迟”—— 只有当程序首次调用获取该单例的接口、真正需要使用这个实例时,才会触发实例的创建逻辑。这种 “按需创建” 的特性,能从根本上避免饿汉式可能出现的 “未使用却占用内存” 的资源浪费问题,也是懒汉式在实际开发中更常用的核心原因。

demo演示

#include <iostream>
#include <string>
#include <mutex>
using namespace std;
class Singleton
{
private:
    Singleton() {}
    Singleton(const Singleton &) = delete;
    Singleton &operator=(const Singleton &) = delete;
    static Singleton *instance;
    static mutex mtx; // 互斥锁:保证多线程下实例创建的线程安全

public:
    // 外部获取实例接口
    static Singleton &getInstance()
    {
        // 双重加锁防止多线程首次判断多次新建实例
        if (instance == nullptr)
        {
            lock_guard<mutex> lock(mtx);
            if (instance == nullptr)
            {
                instance = new Singleton();
            }
        }
        return *instance;
    }
};
Singleton *Singleton::instance = nullptr;
mutex Singleton::mtx;

// 主函数测试
int main()
{
    Singleton &obj1 = Singleton::getInstance();
}

线程池

池化技术

池化技术是一种资源管理优化策略,核心思想可以用一个通俗的比喻理解:

就像餐厅不会在每次有客人点餐时才临时招聘厨师、购买厨具(高成本、慢响应),而是提前招聘好固定数量的厨师、准备好足够的厨具放在 “资源池” 里,客人点餐时直接从池子里调配厨师 / 厨具,用完后放回池子而非销毁 —— 池化技术就是对编程中 “昂贵资源” 的这种 “预创建、复用、统一管理” 的思路。

1. 核心定义(专业表述)

在编程中,针对创建 / 销毁成本高、使用频繁的资源(如线程、数据库连接、内存、网络连接等),提前创建一批资源并放入 “资源池” 中;当程序需要使用资源时,直接从池中获取,使用完毕后不销毁资源,而是归还到池中供后续复用;同时通过统一管理资源池的大小、分配规则,避免资源频繁创建 / 销毁的性能损耗,提升程序整体效率。

线程池创建思路

线程池的创建思路很简单——线程池启动的时候提前创建若干个线程,利用条件变量与循环不断进行任务等待与执行,其实本质上也就是一个生产者-消费者模型,生产者是外部进行线程池调用的线程,消费者就是线程池内的线程对象,_task_queue就是共享缓存区。

这里我们结合单例模式的懒汉方式进行线程池创建,利用之前的日志程序完成有序的消息打印。

代码实战——单例模式结合线程池的使用

线程的封装

#ifndef _THREAD
#define _THREAD
#include <pthread.h>
#include <functional>
#include <string>
#include <iostream>
#include "Log.hpp"
namespace Modelthread
{
    using namespace LogModule;
    using func_t = std::function<void()>;
    static int count = 1;
    class Thread
    {
    private:
        void Enabledetach()
        {
            _isdetach = true;
        }
        void Enablerunning()
        {
            _isrunning = true;
        }
        std::string getname()
        {
            char name[128];
            snprintf(name, sizeof(name), "thread-%d", count++);
            return name;
        }

    public:
        Thread(func_t func) : _pid(0), _isdetach(false), _isrunning(false), _func(func)
        {
            _name = getname();
        }
        static void *routinue(void *x)
        {
            Thread *p = static_cast<Thread *>(x);
            if (p->_isdetach)
            {
                p->Detach();
            }
            pthread_t Pid = p->_pid;
            pthread_setname_np(Pid, p->_name.c_str());
            p->_func();
            return nullptr;
        }
        void Detach()
        {
            pthread_detach(_pid);
            Enabledetach();
        }
        void Start()
        {
            pthread_create(&_pid, nullptr, routinue, this);
            Enablerunning();
        }
        void Stop()
        {
            if (pthread_cancel(_pid) != 0)
            {
                std::cerr << "cancel: " << std::endl;
            }
        }
        bool Join()
        {
            if (_isdetach)
            {
                LOG(LEVEL::INFO) << "thread has been detached!\n";
                return false;
            }
            if (pthread_join(_pid, nullptr) != 0)
            {
                std::cerr << "join: " << std::endl;
                return false;
            }
            else
            {
                LOG(LEVEL::INFO) << _name << " join success\n";
                return true;
            }
            return false;
        }
        ~Thread()
        {
        }

    private:
        pthread_t _pid;
        func_t _func;
        std::string _name;
        bool _isdetach;
        bool _isrunning;
    };
};
#endif

锁的封装

// Mutex.hpp
#pragma once
#include <iostream>
#include <pthread.h>

namespace MutexModule
{
    class Mutex
    {
    public:
        Mutex()
        {
            pthread_mutex_init(&_mutex, nullptr);
        }
        pthread_mutex_t *Get()
        {
            return &_mutex;
        }
        void Lock()
        {
            int n = pthread_mutex_lock(&_mutex);
            (void)n;
        }
        void Unlock()
        {
            int n = pthread_mutex_unlock(&_mutex);
            (void)n;
        }
        ~Mutex()
        {
            pthread_mutex_destroy(&_mutex);
        }

    private:
        pthread_mutex_t _mutex;
    };
    Mutex defaultmutex;
    class LockGuard
    {
    public:
        LockGuard(Mutex &mutex = defaultmutex) : _mutex(mutex)
        {
            _mutex.Lock();
        }
        ~LockGuard()
        {
            _mutex.Unlock();
        }

    private:
        Mutex &_mutex;
    };
}

条件变量的封装

#pragma once

#include <iostream>
#include <pthread.h>
#include "Mutex.hpp"

using namespace MutexModule;

namespace CondModule
{
    class Cond
    {
    public:
        Cond()
        {
            pthread_cond_init(&_cond, nullptr);
        }
        void Wait(Mutex &mutex)
        {
            int n = pthread_cond_wait(&_cond, mutex.Get());
            (void)n;
        }
        void Signal()
        {
            // 唤醒在条件变量下等待的一个线程
            int n = pthread_cond_signal(&_cond);
            (void)n;
        }
        void Broadcast()
        {
            // 唤醒所有在条件变量下等待的线程
            int n = pthread_cond_broadcast(&_cond);
            (void)n;
        }
        ~Cond()
        {
            pthread_cond_destroy(&_cond);
        }

    private:
        pthread_cond_t _cond;
    };
};

生产者线程模拟

#include <functional>
#include <iostream>
#include <vector>
#include <time.h>
#include "Log.hpp"
using namespace LogModule;
void Download()
{
    LOG(LEVEL::INFO) << "我是一个下载任务\n";
}
void Upload()
{
    LOG(LEVEL::INFO) << "我是一个上传任务\n";
}
void Updata()
{
    LOG(LEVEL::INFO) << "我是一个更新任务\n";
}
std::function<void()> func1 = Download;
std::function<void()> func2 = Upload;
std::function<void()> func3 = Updata;
class Task
{
private:
    void init_srand()
    {
        srand((unsigned int)time(0));                                                                                                                                                                                  ;
    }

public:
    Task()
    {
        tasks.push_back(func1);
        tasks.push_back(func2);
        tasks.push_back(func3);
        init_srand();
    }
    // 返回随机任务
    std::function<void()> dispatch()
    {
        int _random = rand() % 3;
        return tasks[_random];
    }
    ~Task()
    {
    }

private:
    std::vector<std::function<void()>> tasks;
};

日志的封装

#pragma once
#include <string>
#include <iostream>
#include "Mutex.hpp"
#include <cstdio>
#include <sstream>
#include <fstream>
#include <unistd.h>
#include <sys/types.h>
#include <memory>
#include <filesystem>
using namespace MutexModule;
namespace LogModule
{
#define gsep "\r\n"
    class LogStrategy
    {
    public:
        ~LogStrategy() = default;
        virtual void SyncLog(const std::string &info) = 0;
    };
    // 显示器打印
    class ScreenStratey : public LogStrategy
    {
    public:
        void SyncLog(const std::string &info) override
        {
            LockGuard _lock(_mutex);
            std::cout << info << gsep;
        }

    private:
        Mutex _mutex;
    };

    // 指定文件打印
    std::string defaultpath = "./Log";
    std::string defaultfile = "Mylog.log";
    class FileStrategy : public LogStrategy
    {
    public:
        FileStrategy(const std::string path = defaultpath, const std::string filename = defaultfile)
            : _path(path),
              _filename(filename)
        {
            if (std::filesystem::exists(_path))
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        void SyncLog(const std::string &message) override
        {
            LockGuard lockguard(_mutex);

            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _filename; // "./log/" + "my.log"
            std::ofstream out(filename, std::ios::app);                                  // 追加写入的 方式打开
            if (!out.is_open())
            {
                return;
            }
            out << message << gsep;
            out.close();
        }
        ~FileStrategy() {};

    private:
        std::string _path;
        std::string _filename;
        Mutex _mutex;
    };
    enum class LEVEL
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    std::string leveltos(const LEVEL &level)
    {
        switch (level)
        {
        case LEVEL::DEBUG:
            return "DEBUG";
        case LEVEL::INFO:
            return "INFO";
        case LEVEL::WARNING:
            return "WARNING";
        case LEVEL::ERROR:
            return "ERROR";
        case LEVEL::FATAL:
            return "FATAL";
        default:
            return "UNKNOW";
        }
    }
    std::string GetTime()
    {
        time_t curr = time(nullptr);
        struct tm curr_tm;
        localtime_r(&curr, &curr_tm);
        char timebuffer[128];
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 curr_tm.tm_year + 1900,
                 curr_tm.tm_mon + 1,
                 curr_tm.tm_mday,
                 curr_tm.tm_hour,
                 curr_tm.tm_min,
                 curr_tm.tm_sec);
        return timebuffer;
    }
    // 默认执行类
    class Logger
    {
    public:
        Logger()
        {
            Enable_Screen_log_Strategy();
        }
        void Enable_Screen_log_Strategy()
        {
            ffulsh_strategy = std::make_unique<ScreenStratey>();
        }
        void Enable_File_log_Strategy()
        {
            ffulsh_strategy = std::make_unique<FileStrategy>();
        }
        class Logmessage
        {
        public:
            Logmessage(const LEVEL &level, std::string filename, int line, Logger &logger)
                : _ctime(GetTime()),
                  _level(level),
                  _pid(getpid()),
                  _filename(filename),
                  _line(line),
                  _logger(logger)

            {
                std::stringstream ss;
                ss << "[" << _ctime << "] "
                   << "[" << leveltos(_level) << "] "
                   << "[" << _pid << "] "
                   << "[" << _filename << "] "
                   << "[" << _line << "] "
                   << "- ";
                finfo = ss.str();
            }
            template <typename T>
            Logmessage &operator<<(const T &info)
            {
                std::stringstream ss;
                ss << info;
                finfo += ss.str();
                return *this;
            }
            ~Logmessage()
            {
                _logger.ffulsh_strategy->SyncLog(finfo);
            }

        private:
            std::string _ctime;
            LEVEL _level;
            pid_t _pid;
            std::string _filename;
            int _line;
            Logger &_logger;
            std::string finfo; // 完整信息
        };
        Logmessage operator()(LEVEL level, std::string name, int line)
        {
            return Logmessage(level, name, line, *this);
        }
        ~Logger()
        {
        }

    private:
        std::unique_ptr<LogStrategy> ffulsh_strategy;
    };
    Logger logger;
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Screen_log_Strategy logger.Enable_Screen_log_Strategy()
#define Enable_File_log_Strategy logger.Enable_File_log_Strategy()
}
// 日志格式:[ctime][level][pid][filename][line]

线程池

#pragma once
#include "Thread.hpp"
#include <vector>
#include "Task.hpp"
#include <queue>
#include <unistd.h>
#include "Log.hpp"
#include "Cond.hpp"
#include "Mutex.hpp"
namespace ThreadPool
{
    using namespace Modelthread;
    using namespace CondModule;
    using namespace MutexModule;
    using namespace LogModule;
    class threadpool
    {
    private:
        void Wakeup_one()
        {
            _cond.Signal();
        }
        void Wakeup_all()
        {
            _cond.Broadcast();
        }
        threadpool()
            : _isrunning(false),
              _sleeping(0),
              _tnum(5)
        {
            for (int i = 1; i <= _tnum; i++)
            {
                _threads.emplace_back(
                    [this]()
                    {
                        Handler();
                    });
            }
        }
        void Start()
        {
            if (_isrunning)
                return;
            _isrunning = true;
            for (auto &_thread : _threads)
            {
                _thread.Start();
                LOG(LEVEL::INFO) << "new thread created!\n";
            }
        }
        threadpool(const threadpool &) = delete;
        threadpool &operator=(const threadpool &) = delete;

    public:
        static threadpool *Getinstance()
        {
            if (inc == nullptr)
            {
                LockGuard _glg(_glock);
                if (inc == nullptr)
                {
                    inc = new threadpool;
                    inc->Start();
                    LOG(LEVEL::INFO) << "首次创建线程池,线程池启动...\n";
                }
            }
            else
            {
                LOG(LEVEL::INFO) << "线程池已创建,返回...\n";
            }
            return inc;
        }
        void Stop()
        {
            if (_isrunning == false)
            {
                return;
            }
            _isrunning = false;
            LOG(LEVEL::INFO) << "线程池即将退出...\n";                                                                                                                                                                    ;
            Wakeup_all();
        }
        void Join()
        {
            for (auto &_thread : _threads)
            {
                _thread.Join();
            }
        }
        bool Enqueue()
        {
            if (_isrunning)
            {
                static Task t;
                {
                    LockGuard _lg(_mutex);
                    _task_queue.push(t.dispatch());
                    if (_sleeping == _threads.size())
                        Wakeup_one();                                                                                                                                                                       ;
                }
                return true;
            }
            return 0;
        }
        void Handler()
        {
            func_t _task;
            while (true)
            {
                {
                    char name[128];
                    pthread_getname_np(pthread_self(), name, sizeof(name));
                    LockGuard _lock(_mutex);
                    while (_task_queue.empty() && _isrunning)
                    {
                        _sleeping++;
                        _cond.Wait(_mutex);                                                                                                                                                                                                         ;
                        _sleeping--;
                    }
                    if (_task_queue.empty() && !_isrunning)
                    {
                        LOG(LEVEL::INFO) << "任务队列为空" << name << "即将退出...\n";
                        break;
                    }
                    _task = _task_queue.front();
                    _task_queue.pop();
                }
                _task();
            }
        }

    private:
        std::vector<Thread> _threads;
        int _tnum;
        bool _isrunning;
        Cond _cond;
        std::queue<func_t> _task_queue;
        Mutex _mutex;
        int _sleeping;
        static threadpool *inc;
        static Mutex _glock;
    };
    threadpool *threadpool::inc = nullptr;
    Mutex threadpool::_glock;
};

主函数调用示例

#include "Log.hpp"
#include "ThreadPool.hpp"
#include <unistd.h>
using namespace ThreadPool;
int main()
{
    threadpool *tp = threadpool::Getinstance();
    int cnt = 10;
    while (cnt--)
    {
        tp->Enqueue();
    }
    sleep(1);
    tp->Stop();
    tp->Join();
}

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐