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

【Linux】多线程_9

文章目录

  • 九、多线程
    • 10. 线程池
  • 未完待续


九、多线程

10. 线程池

这里我没实现一些 懒汉单例模式 的线程池,并且包含 日志打印 的线程池:
Makefile

threadpool:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f threadpool

Thread.hpp

#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>namespace ThreadModule
{// 类型别名using func_t = std::function<void(std::string)>;// 线程类class Thread{public:void Excute(){_func(_threadname);}public:Thread(func_t func, std::string name = "none-name"):_func(func),_threadname(name),_stop(true){}// 执行任务static void *threadroutine(void *args){Thread *self = static_cast<Thread*>(args);self->Excute();return nullptr;}// 启动线程bool Start(){int n = pthread_create(&_tid, nullptr, threadroutine, this);if(!n){_stop = false;return true;}else{return false;}}// 停止线程void Detach(){if(!_stop){pthread_detach(_tid);}}// 等待线程结束void Join(){if(!_stop){pthread_join(_tid, nullptr);}}std::string name(){return _threadname;}void Stop(){_stop = true;}~Thread(){}private:pthread_t _tid;std::string _threadname;func_t _func;bool _stop;};
}#endif

LockGuard.hpp

#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__#include <iostream>
#include <pthread.h>class LockGuard
{
public:// 构造函数加锁LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}// 析构函数解锁~LockGuard(){pthread_mutex_unlock(_mutex);}
private:pthread_mutex_t *_mutex;
};#endif

Log.hpp

#pragma once#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.hpp"// 宏定义,用于定义日志格式
#define LOG(level, format, ...) do{LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__);}while (0)
// 将日志输入到文件
#define EnableFile() do{gIsSave = true;}while (0)
// 将日志输出到显示器
#define EnableScreen() do{gIsSave = false;}while (0)bool gIsSave = false;
// 日志文件名
const std::string logname = "log.txt";// 枚举日志级别
enum Level
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};// 保存日志到文件
void SaveFile(const std::string &filename, const std::string &message)
{std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message;out.close();
}// 日志级别转字符串
std::string LevelToString(int level)
{switch (level){case DEBUG:return "Debug";case INFO:return "Info";case WARNING:return "Warning";case ERROR:return "Error";case FATAL:return "Fatal";default:return "Unknown";}
}// 获取当前时间字符串
std::string GetTimeString()
{time_t curr_time = time(nullptr);struct tm *format_time = localtime(&curr_time);if (format_time == nullptr)return "None";char time_buffer[1024];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",format_time->tm_year + 1900,format_time->tm_mon + 1,format_time->tm_mday,format_time->tm_hour,format_time->tm_min,format_time->tm_sec);return time_buffer;
}// 日志锁,同一时刻只能写一个日志
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;// 日志信息
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{// 日志级别std::string levelstr = LevelToString(level);// 时间std::string timestr = GetTimeString();// 进程idpid_t selfid = getpid();// 日志内容char buffer[1024];va_list arg;va_start(arg, format);vsnprintf(buffer, sizeof(buffer), format, arg);va_end(arg);// 日志格式化std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +"[" + std::to_string(selfid) + "]" +"[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer + "\n";LockGuard lockguard(&lock);// 输出日志if (!issave){std::cout << message;}else{SaveFile(logname, message);}
}

Task.hpp

#pragma once#include <iostream>
#include <string>
#include <functional>class Task
{
public:Task(){}Task(int a, int b):_a(a),_b(b),_result(0){}// 执行加法功能void Excute(){_result = _a + _b;}// 结果std::string ResultToString(){return std::to_string(_a) + "+" + std::to_string(_b) + "=" + std::to_string(_result);}// 问题std::string DebugToString(){return std::to_string(_a) + "+" + std::to_string(_b) + "= ?";}// 重载()运算符void operator()(){Excute();}
private:int _a;int _b;int _result;
};

ThreadPool.hpp

#pragma once#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.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);}// 私有构造函数ThreadPool(int threadnum = gdefaultthreadnum):_threadnum(threadnum),_waitnum(0),_isrunning(false){// 初始化锁pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);// 日志LOG(INFO, "ThreadPool Construct()");}// 初始化线程池void InitThreadPool(){// 创建一批线程for (int num = 0; num < _threadnum; num++){std::string name = "thread-" + std::to_string(num + 1);_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();}}// 任务处理函数void HandlerTask(std::string name) // 类的成员方法,也可以成为另一个类的回调方法,方便我们继续类级别的互相调用!{// 日志LOG(INFO, "%s is running...", name.c_str());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<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(const ThreadPool<T> &) = delete;
public:static ThreadPool<T> *GetInstance(){// 首次使用时,创建线程池单例if (nullptr == _instance){// 对于多线程创建单例时加锁,保证线程安全LockGuard lockguard(&_lock);if (nullptr == _instance){// 创建线程池实例_instance = new ThreadPool<T>();_instance->InitThreadPool();_instance->Start();LOG(DEBUG, "创建线程池单例");return _instance;}}// 已经创建过线程池单例,直接返回LOG(DEBUG, "获取线程池单例");return _instance;}// 停止线程池void Stop(){LockQueue();_isrunning = false;ThreadWakeupAll();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;// 等待线程数int _waitnum;// 线程池是否运行bool _isrunning;// 线程池单例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;

Main.cc

#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Log.hpp"
#include <iostream>
#include <string>
#include <memory>
#include <ctime>int main()
{// 日志LOG(DEBUG, "程序已经加载");sleep(2);// 创建线程池单例ThreadPool<Task>::GetInstance();sleep(2);// 获取单例ThreadPool<Task>::GetInstance();sleep(2);ThreadPool<Task>::GetInstance();sleep(2);ThreadPool<Task>::GetInstance();sleep(2);// 等待线程结束ThreadPool<Task>::GetInstance()->Wait();sleep(2);return 0;
}

结果演示:
在这里插入图片描述


未完待续

相关文章:

【Linux】多线程_9

文章目录 九、多线程10. 线程池 未完待续 九、多线程 10. 线程池 这里我没实现一些 懒汉单例模式 的线程池&#xff0c;并且包含 日志打印 的线程池&#xff1a; Makefile&#xff1a; threadpool:Main.ccg -o $ $^ -stdc11 -lpthread .PHONY:clean clean:rm -f threadpoolT…...

LabVIEW设备检修信息管理系统

开发了基于LabVIEW设计平台开发的设备检修信息管理系统。该系统应用于各种设备的检修基地&#xff0c;通过与基地管理信息系统的连接和数据交换&#xff0c;实现了本地检修工位数据的远程自动化管理&#xff0c;提高了设备的检修效率和安全性。 项目背景 现代设备运维过程中信…...

python爬虫基础:使用lxml库进行HTML解析和数据提取的实践指南

使用lxml库进行HTML解析和数据提取的实践指南 在Python编程中&#xff0c;网页抓取和数据提取是一项常见任务。lxml库因其高效性和强大的XPath支持&#xff0c;成为了处理HTML和XML文档的优选工具。本文将带你了解如何使用lxml来解析HTML文档并提取所需数据。 1. 安装lxml库 …...

大语言模型系列:Transformer

在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Transformer模型自2017年由Vaswani等人在论文《Attention Is All You Need》中提出以来&#xff0c;已成为最具影响力的技术之一。这种模型设计的核心是自注意力机制&#xff0c;它允许模型在处理序列数据时&#xf…...

宠物健康新守护:智能听诊器引领科技突破

在宠物护理领域&#xff0c;一项令人瞩目的科技创新正逐渐兴起&#xff0c;那便是智能听诊器。这款革命性的设备以前所未有的准确性和便利性&#xff0c;为宠物主人提供了一种全新的健康监测体验。 只需将智能听诊器轻轻放置在爱宠的身上&#xff0c;它便立即开始工作&#xf…...

KITTI 3D 数据可视化

引言 KITTI 视觉基准测试套件&#xff08;KITTI Vision Benchmark Suite&#xff09;提供了大量用于理解自动驾驶场景的工具。尤其是3D数据可视化在分析和解释传感器&#xff08;如激光雷达&#xff09;与环境的复杂交互中起到了至关重要的作用。本文将详细探讨KITTI数据集中3…...

旅游数据可视化:免费工具让复杂数据变得简单易懂

随着旅游业的蓬勃发展&#xff0c;海量的数据如同繁星点点&#xff0c;记录着每一位旅者的足迹与偏好。然而&#xff0c;如何将这些复杂的数据转化为直观、易懂的信息&#xff0c;为旅游企业精准决策、为消费者提供更加个性化的服务&#xff0c;成为了行业内外共同关注的焦点。…...

数据结构进阶:使用链表实现栈和队列详解与示例(C, C#, C++)

文章目录 1、 栈与队列简介栈&#xff08;Stack&#xff09;队列&#xff08;Queue&#xff09; 2、使用链表实现栈C语言实现C#语言实现C语言实现 3、使用链表实现队列C语言实现C#语言实现C语言实现 4、链表实现栈和队列的性能分析时间复杂度空间复杂度性能特点与其他实现的比较…...

【线程系列之五】线程池介绍C语言

一、基本概念 1.1 概念 线程池&#xff08;Thread Pool&#xff09;是一种基于池化技术管理线程的机制&#xff0c;旨在减少线程创建和销毁的开销&#xff0c;提高系统资源的利用率&#xff0c;以及更好地控制系统中同时运行的线程数量。线程池通过预先创建一定数量的线程&am…...

【学习css3】使用flex和grid实现等高元素布局

过往的实现方法是使用浮动加计算布局来实现&#xff0c;当flex和grid问世时&#xff0c;这一切将变得简单起来 一、简单的两列实现 1、先看页面效果 2、css代码 .container {padding: 10px;width: 100ch;margin: 0 auto;box-shadow: inset 0 0 0 2px #ccc;}.column {margin: 2…...

如何防止Eclipse格式化程序在行注释开头插入空格

格式化前&#xff1a; //foo bar 格式化后&#xff1a; // foo bar 这种看着不是很舒服。如果不让格式化时自动在注释符后面插入空格呢&#xff1f; 要在Eclipse中进行代码格式化时防止在行注释&#xff08;‌//&#xff09;‌后面自动增加空格&#xff0c;‌可以通过调整…...

Nextjs 调用组件内的方法

在 Next.js 中&#xff0c;如果你想从一个组件外部调用组件内部的方法&#xff0c;可以使用 React 的 useRef 钩子来引用组件实例并调用其方法。这种方法主要适用于类组件&#xff0c;但也可以用于函数组件&#xff0c;通过将方法暴露在 ref 对象上。 以下是一个示例&#xff…...

ip地址是电脑还是网线决定的

在数字化时代的浪潮中&#xff0c;网络已经成为了我们日常生活和工作不可或缺的一部分。当我们谈论网络时&#xff0c;IP地址无疑是一个核心的概念。然而&#xff0c;关于IP地址的分配和决定因素&#xff0c;很多人可能存在误解。有些人认为IP地址是由电脑决定的&#xff0c;而…...

Hadoop中HDFS、Hive 和 HBase三者之间的关系

HDFS&#xff08;Hadoop Distributed File System&#xff09;、Hive 和 HBase 是 Hadoop 生态系统中三个重要的组件&#xff0c;它们各自解决了大数据存储和处理的不同层面的问题。我们用大白话来解释这三个组件之间的关系&#xff1a; HDFS - 数据的仓库&#xff1a; HDFS 是…...

opencv—常用函数学习_“干货“_10

目录 二七、离散余弦变换 执行离散余弦变换 (dct) 和逆变换 (idct) 解释 实际应用 JPEG压缩示例&#xff08;简化版&#xff09; 二八、图像几何变换 仿射变换 (warpAffine 和 getAffineTransform) 透视变换 (warpPerspective 和 getPerspectiveTransform) 旋转变换 (g…...

Jmeter二次开发Demo

Jmeter二次开发Demo 前言 在上一集&#xff0c;我们已经完成了JMX脚本的分析&#xff0c;大致了解了JMX脚本的基本元素。 那么在这一集&#xff0c;我们将会介绍一下Jmeter二次开发的Demo。 Demo代码 那么话不多说&#xff0c;我们就直接上代码。 public class TestStress…...

MongoDB综合实战篇(超容易)

一、题目引入 在MongoDB的gk集合里插入以下数据&#xff1a; 用语句完成如下功能&#xff1a; &#xff08;1&#xff09;查询张三同学的成绩信息 &#xff08;2&#xff09;查询李四同学的语文成绩 &#xff08;3&#xff09;查询没有选化学的同学 &#xff08;4&#xf…...

框架设计MVVM

重点&#xff1a; 1.viewmodel 包含model 2.view包含viewmodel,通过驱动viewmodel去控制model的数据和业务逻辑 // Test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#include <iostream> #include <vector>using namespace std;#p…...

RK3399基础部分

1.RK3399介绍 基础特性&#xff1a; 高达1.8GHz的双核Cortex-A72 四核Cortex-A53高达1.4GHz NPU高达3.0TOPS Mali-T860MP4 GPU 双通道DDR3/DDR3L/LPDDR3/LPDDR4 4K超高清H265/H264/VP9 HDR10/HLG H264编码器 双MIPI CSI和ISP USB Type-CGPU: 图形处理器&#xff08;英语&…...

linux高级编程(广播与组播)

广播与组播&#xff1a; 广播&#xff1a; 局域网&#xff0c;一个人发所有人都能收&#xff08;服务器找客户端&#xff09;&#xff0c;&#xff08;发给路由器的广播地址后后路由器自动给所有人发&#xff0c;可用于服务器找客户端&#xff09; 只能udp来做 setsocketopt…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南

1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发&#xff0c;使用DevEco Studio作为开发工具&#xff0c;采用Java语言实现&#xff0c;包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...