C++11手撕线程池 call_once 单例模式 Singleton / condition_variable 与其使用场景

news2024/9/30 17:38:53

一、call_once 单例模式 Singleton 

大家可以先看这篇文章:https://zh.cppreference.com/w/cpp/thread/call_once

/*
    std::call_once
    void call_once( std::once_flag& flag, Callable&& f, Args&&... args );
*/
#include <iostream>
#include <mutex>
#include <thread>

std::once_flag flag1, flag2;

void simple_do_once() {
    std::call_once(flag1, []() {
        std::cout << "简单样例:调用一次\n";
    });
}

void test1() {
    std::thread st1(simple_do_once);
    std::thread st2(simple_do_once);
    std::thread st3(simple_do_once);
    std::thread st4(simple_do_once);
    st1.join();
    st2.join();
    st3.join();
    st4.join();
}

void may_throw_function(bool do_throw) {
    if (do_throw) {
        std::cout << "抛出:call_once 会重试\n"; // 这会出现不止一次
        throw std::exception();
    }
    std::cout << "没有抛出,call_once 不会再重试\n"; // 保证一次
}

void do_once(bool do_throw) {
    try {
        std::call_once(flag2, may_throw_function, do_throw);
    }
    catch (...) {}
}

void test2() {
    std::thread t1(do_once, true);
    std::thread t2(do_once, true);
    std::thread t3(do_once, false);
    std::thread t4(do_once, true);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}
int main() {
    test1();
    test2();
    return 0;
}

call_once 应用在单例模式,以及关于单例模式我的往期文章推荐:C++ 设计模式----“对象性能“模式_爱编程的大丙 设计模式-CSDN博客icon-default.png?t=N7T8https://heheda.blog.csdn.net/article/details/131466271

懒汉是一开始不会实例化,什么时候用就什么时候new,才会实例化
饿汉在一开始类加载的时候就已经实例化,并且创建单例对象,以后只管用即可
--来自百度文库
#include <iostream>
#include <thread>
#include <mutex>
#include <string>

// 日志类:在整个项目中,有提示信息或者有报错信息,都通过这个类来打印
// 这些信息到日志文件,或者打印到屏幕上。显然,全局只需要一个日志类的
// 对象就可以完成所有的打印操作了。不需要第二个类来操作,这个时候就可以
// 使用单例模式来设计它

std::once_flag onceFlag;
class Log {
public:
    Log(const Log& log) = delete;
    Log& operator=(const Log& log) = delete;
    // static Log& getInstance() { 
    //     static Log log; // 饿汉模式
    //     return log;
    // }
    static Log& getInstance() { // 懒汉模式
        std::call_once(onceFlag, []() {
            std::cout << "简单样例:调用一次\n";
            log = new Log;
        });
        return *log;
    }
    void PrintLog(std::string msg) {
        std::cout << __TIME__ << msg << std::endl;
    }
private:
    Log() {};
    static Log* log; 
};
Log* Log::log = nullptr;

void func() {
    Log::getInstance().PrintLog("这是一个提示");
}

void print_error() {
    Log::getInstance().PrintLog("发现一个错误");
}

void test() {
    std::thread t1(print_error);
    std::thread t2(print_error);
    std::thread t3(func);
    t1.join();
    t2.join();
    t3.join();
}

int main() {
    test();
    return 0;
}

二、condition_variable 与其使用场景

#include <iostream>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <queue>

std::queue<int> queue;
std::condition_variable cond;
std::mutex mtx;

void Producer() {
    for (int i = 0; i < 10; i++) {
        {
            std::unique_lock<std::mutex> locker(mtx);
            queue.push(i);
            cond.notify_one();
            std::cout << "Producer : " << i << std::endl;
        }
        std::this_thread::sleep_for(std::chrono::microseconds(100));
    }
}

void Consumer() {
    while (1) {
        std::unique_lock<std::mutex> locker(mtx);
        cond.wait(locker, []() {
            return !queue.empty();
            });
        int value = queue.front();
        queue.pop();
        std::cout << "Consumer :" << value << std::endl;
    }
}

int main() {
    std::thread t1(Producer);
    std::thread t2(Consumer);
    t1.join();
    t2.join();
    return 0;
}

三、C++11 手撕线程池 + 单例模式(call_once)

  • ThreadPool.h
#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <condition_variable>
#include <queue>
#include <vector>
#include <functional>
std::once_flag onceFlag;
class ThreadPool {
private:
    ThreadPool();
public:
    ThreadPool(const ThreadPool& obj) = delete;
    ThreadPool& operator=(const ThreadPool& obj) = delete;
    static ThreadPool& getInstance();
    ~ThreadPool();
    template<class F, class... Args>
    void enqueue(F&& f, Args&&... args) {
        std::function<void()> task =
            std::bind(std::forward<F>(f), std::forward<Args>(args)...);
        {
            std::unique_lock<std::mutex> locker(mtx);
            tasks.emplace(std::move(task));
        }
        cond.notify_one();
    }
    inline void setNum(int num) {
        threadNum = num;
    }
    inline void printNum() {
        std::cout << "线程数量为:" << threadNum << std::endl;
    }
private:
    static ThreadPool* pool;
    std::vector<std::thread> threads;// 线程数组
    std::queue<std::function<void()>> tasks;//任务队列
    std::mutex mtx;// 互斥锁
    std::condition_variable cond;//条件变量
    bool stop;
    int threadNum;// 线程数量
};

  • ThreadPool.cpp
#include "ThreadPool.h"
#include <iostream>
ThreadPool* ThreadPool::pool = nullptr;
ThreadPool::ThreadPool() {
    stop = false;
    threadNum = 4;
    for (int i = 0; i < threadNum; ++i) {
        threads.emplace_back([this]() {
            while (1) {
                std::unique_lock<std::mutex> locker(mtx);
                cond.wait(locker, [this]() {
                    return !tasks.empty() || stop;
                    });
                if (stop && tasks.empty()) {
                    return;
                }
                std::function<void()> task(std::move(tasks.front()));
                tasks.pop();
                task();// 执行这个任务
            }
            });
    }
}

ThreadPool::~ThreadPool() {
    {
        std::unique_lock<std::mutex> locker(mtx);
        stop = true;
    }
    cond.notify_all();
    for (auto& t : threads) {
        t.join();
    }
}

ThreadPool& ThreadPool::getInstance() { // 懒汉模式
    std::call_once(onceFlag, []() {
        std::cout << "懒汉模式:调用一次" << std::endl;
        pool = new ThreadPool();
        });
    return *pool;
}
  •  main.cpp
#include <iostream>
#include "ThreadPool.h"
#include <thread>
void addTask() {
    ThreadPool& pool = ThreadPool::getInstance();
    pool.setNum(8);
    for (int i = 0; i < 10; ++i) {
        pool.enqueue([i]() {
            std::cout << "task : " << i << " is runing!" << std::endl;
            std::this_thread::sleep_for(std::chrono::microseconds(10));
            std::cout << "task : " << i << " is done!" << std::endl;
            });
    }
}

void test() {
    std::thread t1(addTask);
    std::thread t2(addTask);
    std::thread t3(addTask);
    t1.join();
    t2.join();
    t3.join();
}

int main() {
    test();
    return 0;
}

运行结果:

懒汉模式:调用一次
task : 0 is runing!
task : 0 is done!
task : 0 is runing!
task : 0 is done!
task : 1 is runing!
task : 1 is done!
task : 0 is runing!
task : 0 is done!
task : 1 is runing!
task : 1 is done!
task : 1 is runing!
task : 1 is done!
task : 2 is runing!
task : 2 is done!
task : 3 is runing!
task : 3 is done!
task : 2 is runing!
task : 2 is done!
task : 4 is runing!
task : 4 is done!
task : 3 is runing!
task : 3 is done!
task : 2 is runing!
task : 2 is done!
task : 3 is runing!
task : 3 is done!
task : 4 is runing!
task : 4 is done!
task : 5 is runing!
task : 5 is done!
task : 4 is runing!
task : 4 is done!
task : 5 is runing!
task : 5 is done!
task : 6 is runing!
task : 6 is done!
task : 7 is runing!
task : 7 is done!
task : 8 is runing!
task : 8 is done!
task : 9 is runing!
task : 9 is done!
task : 6 is runing!
task : 6 is done!
task : 7 is runing!
task : 7 is done!
task : 5 is runing!
task : 5 is done!
task : 6 is runing!
task : 6 is done!
task : 8 is runing!
task : 8 is done!
task : 9 is runing!
task : 9 is done!
task : 7 is runing!
task : 7 is done!
task : 8 is runing!
task : 8 is done!
task : 9 is runing!

D:\Work\vsproject\c++11\x64\Debug\c++11.exe (进程 32636)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

未完待续~ 

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

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

相关文章

龙湖集团2023年销售额减少约14%:股价屡创新低,股东难言满意

近日&#xff0c;龙湖集团控股有限公司&#xff08;HK:00960&#xff0c;下称“龙湖集团”或“龙湖”&#xff09;发布了2023年未经审核运营数据。2023年&#xff0c;龙湖集团实现经营性收入248.7亿元&#xff0c;同比增加6.28%&#xff1b;其中运营收入约129.4亿元&#xff0c…

CM11 链表分割

链表分割_牛客题霸_牛客网 (nowcoder.com) 一、思路分析 二、源码 一、思路分析 创建两个链表small、big 遍历原来链表 比X小的节点尾插到small 比X大的节点尾插到big 最后来链接起来 这样不会改变各个节点的相对顺序 二、源码 ListNode* partition(ListNode* pHead,…

Java21 + SpringBoot3集成easy-captcha实现验证码显示和登录校验

文章目录 前言相关技术简介easy-captcha 实现步骤引入maven依赖定义实体类定义登录服务类定义登录控制器前端登录页面实现测试和验证 总结附录使用Session缓存验证码前端登录页面实现代码 前言 近日心血来潮想做一个开源项目&#xff0c;目标是做一款可以适配多端、功能完备的…

【Linux C | 进程】进程环境 | 什么是进程?进程的开始、终止、存储空间布局、命令行参数、环境变量

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

如何无公网ip实现SSH远程访问本地局域网openEuler系统?

文章目录 1. 本地SSH连接测试2. openEuler安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 欧拉操作系统(openEuler, 简称“欧拉”)是面向数字基础设施的操作系统,支持服务器、云计算、边缘openEuler是面向数字基础设施的操作系…

2023.1.17 关于 Redis 持久化 AOF 策略详解

目录 引言 AOF 策略 实例演示一 缓冲区 重写机制 手动触发 自动触发 AOF 重写流程 实例演示二 引言 Redis 实现持久化的两大策略 RDB ——> Redis DataBase&#xff08;定期备份&#xff09;AOF ——> Append Only File&#xff08;实时备份&#xff09; 注意&…

Linux中的软件包管理器yum

目录 1.什么是软件包 2.关于 rzsz 3.查看软件包 4.如何安装软件 5.如何卸载软件 1.什么是软件包 ● 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. ● 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理…

EasyExcelFactory 导入导出功能的实战使用

EasyExcelFactory 导入导出功能的实战使用分享&#xff1a; 1、jar包引入 <!-- 阿里巴巴Excel处理--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.6</version></dependen…

华为AC+FIT AP组网配置

AC配置 vlan batch 100 to 101dhcp enableip pool apgateway-list 192.168.100.254 network 192.168.100.0 mask 255.255.255.0 interface Vlanif100ip address 192.168.100.254 255.255.255.0dhcp select globalinterface GigabitEthernet0/0/1port link-type trunkport trun…

本地部署轻量级web开发框架Flask并实现无公网ip远程访问开发界面

文章目录 1. 安装部署Flask2. 安装Cpolar内网穿透3. 配置Flask的web界面公网访问地址4. 公网远程访问Flask的web界面 本篇文章主要讲解如何在本地安装Flask&#xff0c;以及如何将其web界面发布到公网进行远程访问。 Flask是目前十分流行的web框架&#xff0c;采用Python编程语…

14027.ptp 控制流

文章目录 1 ptp 控制流1.1 控制流分层 1 ptp 控制流 1.1 控制流分层 大体分为4层&#xff1a;1 ptp4l层&#xff1a; 获取配置文件、创建时钟、poll监控文件描述符。2 clock时钟层&#xff1a;提供提供clock_poll、clock_create、clock_sync 等3 port 端口层&#xff1a;port…

数据结构与算法:图

文章目录 图1) 概念有向 vs 无向度权路径环图的连通性 2) 图的表示3) Java 表示4) DFS5) BFS6) 拓扑排序7) 最短路径DijkstraBellman-FordFloyd-Warshall 8) 最小生成树PrimKruskal 图 1) 概念 图是由顶点&#xff08;vertex&#xff09;和边&#xff08;edge&#xff09;组成…

如何搭建MariaDB并实现无公网ip环境远程连接本地数据库

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 1. 配置MariaDB数据库1.1 安装MariaDB数据库1.2 测试局域网内远程连接 2. 内网穿透2.1 创建隧道映射…

【C++】stack与queue的模拟实现

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 前言 stack与queue的实现比较简…

Mysql - 定点型(DECIMAL)的使用详解及练习

目录 &#x1f436;1. 前言&#xff1a; &#x1f436;2. DECIMAL类型简介 &#x1f436;3. Decimal使用实战 &#x1f96a;#结论1&#xff1a;小数位不足会自动补0 &#x1f96a;#结论2&#xff1a;小数位超出会截断 并按四舍五入处理。 &#x1f96a;#结论3&#xff1…

代码随想录算法训练营第34天 |1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果

1005.K次取反后最大化的数组和 题目链接&#xff1a;1005.K次取反后最大化的数组和 给定一个整数数组 A&#xff0c;我们只能用以下方法修改该数组&#xff1a;我们选择某个索引 i 并将 A[i] 替换为 -A[i]&#xff0c;然后总共重复这个过程 K 次。&#xff08;我们可以多次选…

【明道云】学习笔记1-了解APaaS

【背景】 APaaS (Application Platform As A Service) &#xff0c;即应用程序平台即服务&#xff0c;这是基于PaaS&#xff08;平台即服务&#xff09;的一种解决方案&#xff0c;支持应用程序在云端的开发、部署和运行&#xff0c;提供软件开发中的基础工具给用户&#xff0…

【JavaEE进阶】 Spring Boot⽇志

文章目录 &#x1f38b;关于日志&#x1f6a9;为什么要学习⽇志&#x1f6a9;⽇志的⽤途&#x1f6a9;日志的简单使用 &#x1f384;打印⽇志&#x1f6a9;程序中得到⽇志对象&#x1f6a9;使⽤⽇志对象打印⽇志 &#x1f38d;⽇志格式的说明&#x1f6a9;⽇志级别的作用&#…

Java - 深入理解加密解密和签名算法

文章目录 应用的接口安全性问题可能来源加密解密Why保护数据隐私防止未经授权的访问防止数据泄露 对称加密 VS 单向加密 VS 非对称加密一、对称加密二、单向加密&#xff08;哈希加密&#xff09;三、非对称加密 常用的对称加密算法1. AES&#xff08;高级加密标准&#xff09;…

【Java】学习一门开发语言,从TA的Hello World开始

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《Java》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握…