線程池單例模式

線程池的概念? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

線程池是一種線程使用模式。?

一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護著多個線程,等待著監督管理者分配可并發執行的任務。

  • 這避免了在處理短時間任務時創建與銷毀線程的代價。
  • 線程池不僅能夠保證內核的充分利用,還能防止過分調度。

tips:可用線程數量應該取決于可用的并發處理器、處理器內核、內存、網絡sockets等的數量。

線程池的應用場景

  • 需要大量的線程來完成任務,且完成任務的時間比較短。 WEB服務器完成網頁請求這樣的任務,使用線程池技術是非常合適的。因為單個任務小,而任務數量巨大,你可以想象一個熱門網站的點擊次數。 但對于長時間的任務,比如一個 Telnet連接請求,線程池的優點就不明顯了。因為Telnet會話時間比線程的創建時間大多了。
  • 對性能要求苛刻的應用,比如要求服務器迅速響應客戶請求。
  • 接受突發性的大量請求,但不至于使服務器因此產生大量線程的應用。突發性大量客戶請求,在沒有線程池情況下,將產生大量線程,雖然理論上大部分操作系統線程數目最大值不是問題,短時間內產生大量線程可能使內存到達極限,出現錯誤。

線程池的實現

下面我們實現一個簡單的線程池,線程池中提供了一個任務隊列,以及若干個線程(多線程)。

  • 線程池中的多個線程負責從任務隊列當中拿任務,并將拿到的任務進行處理。
  • 線程池對外提供一個Push接口,用于讓外部線程能夠將任務Push到任務隊列當中。

線程池代碼如下:

#include <iostream>
#include <pthread.h>
#include <vector>
#include <queue>
#include <string>static const int defaultnum = 5;struct ThreadInfo
{pthread_t tids;std::string NameThread;
};template<class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&lock);}void UnLock(){pthread_mutex_unlock(&lock);}void Wait(){pthread_cond_wait(&cond, &lock);}void WakeUp(){pthread_cond_signal(&cond);}bool IsEmptyTask(){return _TasksQueue.size() == 0;}public:ThreadPool(int num = defaultnum): _threads(num){pthread_mutex_init(&lock, nullptr);pthread_cond_init(&cond, nullptr);}void ThreadStart(){int n = _threads.size();for(int i = 0; i < n; i++){pthread_create(&_threads[i].tids, nullptr, ThreadTasks, this);_threads[i].NameThread = "thread-" + std::to_string(i);}}std::string GetThreadName(pthread_t tid){for(auto& e : _threads){if(tid == e.tids){return e.NameThread; }}return "none";}static void *ThreadTasks(void* args){ThreadPool<T>* TP = static_cast<ThreadPool<T>*>(args);while(true){std::string Name = TP->GetThreadName(pthread_self());TP->Lock();while(TP->IsEmptyTask()){TP->Wait();}T t = TP->pop();TP->UnLock();t();std::cout << Name.c_str() << ' ' << std::endl;t.GetTask();}}T pop(){T t = _TasksQueue.front();_TasksQueue.pop();return t;}void push(const T &task){Lock();_TasksQueue.push(task);WakeUp();UnLock();}~ThreadPool(){pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);}
private:std::vector<ThreadInfo> _threads;std::queue<T> _TasksQueue;pthread_mutex_t lock;pthread_cond_t cond;
};

為什么線程池中需要有互斥鎖和條件變量?

使用互斥鎖的原因:STL容器一開始被設計時,就是為了追求效率,并沒有考慮線程安全,多線程場景,pop數據時可能產生并發問題

使用條件變量的原因:

為什么線程池中的線程執行例程需要設置為靜態方法?

Routine作為類的成員函數,該函數的第一個參數是隱藏的this指針,因此這里的Routine函數,雖然看起來只有一個參數,而實際上它有兩個參數,此時直接將該Routine函數作為創建線程時的執行例程是不行的,無法通過編譯。

靜態成員函數屬于類,而不屬于某個對象,也就是說靜態成員函數是沒有隱藏的this指針的,因此我們需要將Routine設置為靜態方法,此時Routine函數才真正只有一個參數類型為void*的參數。

但是在靜態成員函數內部無法調用非靜態成員函數,而我們需要在Routine函數當中調用該類的某些非靜態成員函數,比如Pop。因此我們需要在創建線程時,向Routine函數傳入的當前對象的this指針,此時我們就能夠通過該this指針在Routine函數內部調用非靜態成員函數了。

任務類型的設計

#pragma once#include "ThreadPool.hpp"enum
{EXITCODE = 0,DIVZERO,MODZERO
};class Task
{
public:Task(int x, int y, char oper, int exitcode_ = EXITCODE) : _data1(x), _data2(y), _oper(oper), exitcode(exitcode_){}void run(){switch (_oper){case '+':result = _data1 + _data2;break;case '-':result = _data1 - _data2;break;case '*':result = _data1 * _data2;break;case '/':if(_data1 == 0 | _data2 == 0){exitcode = DIVZERO;}else{result = _data1 / _data2;}break;case '%':if(_data1 == 0 | _data2 == 0){exitcode = MODZERO;}else{result = _data1 % _data2;}break;default:std::cout << "Symbol mismatch!" << std::endl;break;}SolveTask();}std::string _To_String(){std::string str;str += "[exitcode: ";str += std::to_string(exitcode);str += "]";str += " ";str += std::to_string(_data1);str += " ";str += _oper;str += " ";str += std::to_string(_data2);return str;}void GetTask(){std::cout << _data1 << " " << _oper << " " << _data2 << " = ?" << std::endl;}void SolveTask(){std::cout << _To_String() << " = " << result << std::endl;}void operator()(){run();}~Task(){}private:int _data1;int _data2;char _oper;int exitcode;int result;
};

主線程邏輯

主線程就負責不斷向任務隊列當中Push任務就行了,此后線程池當中的線程會從任務隊列當中獲取到這些任務并進行處理。

#include "SingletonThreadPool.hpp"
#include "Task.hpp"
#include <unistd.h>std::string oper = "+-*/%";int main()
{srand(time(nullptr));SingletonThreadPool<Task>* STP = new SingletonThreadPool<Task>(5);STP->ThreadStart();int len = oper.size(); while(true){sleep(1);int data1 = rand() % 10;int data2 = rand() % 10 + 1;char op = oper[rand() % len];Task t(data1, data2, op);t.push(t);t.GetTask();}return 0;
}

運行代碼后一瞬間就有六個線程,其中一個是主線程,另外五個是線程池內處理任務的線程。

注意:?此后我們如果想讓線程池處理其他不同的任務請求時,我們只需要提供一個任務類,在該任務類當中提供對應的任務處理方法就行了。

線程安全的單例模式

什么是單例模式

單例模式是一種 "經典的, 常用的, 常考的" 設計模式.

什么是設計模式

IT行業這么火, 涌入的人很多. 俗話說林子大了啥鳥都有. 大佬和菜雞們兩極分化的越來越嚴重. 為了讓菜雞們不太拖大佬的后腿, 于是大佬們針對一些經典的常見的場景, 給定了一些對應的解決方案, 這個就是 設計模式

單例模式的特點

某些類, 只應該具有一個對象(實例), 就稱之為單例.?在很多服務器開發場景中, 經常需要讓服務器加載很多的數據 (上百G) 到內存中. 此時往往要用一個單例的類來管理這些數據.

餓漢實現方式和懶漢實現方式

餓漢方式就是直接在類中將需要使用的對象先申請好

懶漢方式最核心的思想是 "延時加載". 從而能夠優化服務器的啟動速度.

例如:

#pragma onceclass Singleton
{
public:Singleton(){std::cout << "對象創建成功" << std::endl;}static Singleton& GetInstance(){return num;}private:static Singleton num;
};
#include <iostream>using namespace std;
#include "singleton.hpp"//static int num = 10;Singleton init1;
Singleton init2;
Singleton init3;
Singleton init4;
Singleton init5;
Singleton init6;int main()
{printf("啟動!!!\n");    return 0;
}

可以看到餓漢式的單例模式會在程序啟動之前對象就創建好了

餓漢方式實現單例模式

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

只要通過 Singleton 這個包裝類來使用 T 對象, 則一個進程中只有一個 T 對象的實例

懶漢方式實現單例模式

template <typename T> class Singleton
{static T* inst; 
public:static T* GetInstance() {if(inst == NULL) {inst = new T();}return inst;} 
};

存在一個嚴重的問題, 線程不安全.
第一次調用 GetInstance 的時候, 如果兩個線程同時調用, 可能會創建出兩份 T 對象的實例.

懶漢方式實現單例模式(線程安全版本)

// 懶漢模式, 線程安全 
template <typename T> 
class Singleton { volatile static T* inst;  // 需要設置 volatile 關鍵字, 否則可能被編譯器優化. static std::mutex lock; 
public: static T* GetInstance(){ if(inst == NULL)  //避免占用CPU和操作系統資源,做無意義的事,提高效率{  lock.lock();          // 使用互斥鎖, 保證多線程情況下也只調用一次 new.if (inst == NULL) { inst = new T(); } lock.unlock(); } return inst; } 
}; 

注意事項:

1. 加鎖解鎖的位置

2. 雙重 if 判定, 避免不必要的鎖競爭

3. volatile關鍵字防止過度優化

將上述線程池代碼改為單例模式

#pragma once#include <iostream>
#include <pthread.h>
#include <queue>
#include <vector>
#include <string>static const int defaultnum = 5;class ThreadInfo
{
public:pthread_t tid;std::string threadname;
};template <class T>
class SingletonThreadPool
{void Lock(){pthread_mutex_lock(&lock);}void UnLock(){pthread_mutex_unlock(&lock);}void Wait(){pthread_cond_wait(&cond, &lock);}void WakeUp(){pthread_cond_signal(&cond);}bool IsEmptyThreadPool(){return _tasksqueue.size() == 0;}public:static void* RoutineTasks(void* args){SingletonThreadPool<T> *TP = static_cast<SingletonThreadPool<T>*>(args);while(true){std::string name = TP->GetThreadName(pthread_self());TP->Lock();if(TP->IsEmptyThreadPool()){TP->Wait();}T t = TP->pop();TP->UnLock();t();std::cout << name << ' ' << std::endl;t.GetTask();}}public:void ThreadStart(){int num = _threads.size();for(int i = 0; i < num; i++){pthread_create(&_threads[i].tid, nullptr, RoutineTasks, this);_threads[i].threadname = "thread-" + std::to_string(i);}}T pop(){T task = _tasksqueue.front();_tasksqueue.pop();return task;}std::string GetThreadName(pthread_t tid){for(const auto& e : _threads){if(tid == e.tid)return e.threadname;}return "none";}void push(const T& task){Lock();_tasksqueue.push(task);WakeUp();UnLock();}static SingletonThreadPool<T>* GetInStance(){//避免占用CPU和操作系統資源,做無意義的事,提高效率if(inst == nullptr){//避免多線程模式下,同時申請多個instpthread_mutex_lock(&slock);if(inst == nullptr){inst = new SingletonThreadPool<T>;}pthread_mutex_unlock(&slock);}return inst;}private:SingletonThreadPool(int num = defaultnum):_threads(num){pthread_mutex_init(&lock, nullptr);pthread_cond_init(&cond, nullptr);}~SingletonThreadPool(){pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);}//防拷貝SingletonThreadPool(const SingletonThreadPool<T>& STP) = delete;SingletonThreadPool<T>& operator=(const SingletonThreadPool<T>& STP) = delete;private:std::vector<ThreadInfo> _threads;std::queue<T> _tasksqueue;pthread_mutex_t lock;pthread_cond_t cond;static SingletonThreadPool<T> *inst;static pthread_mutex_t slock;
}; template<class T>
SingletonThreadPool<T>* SingletonThreadPool<T>::inst = nullptr;template<class T>
pthread_mutex_t SingletonThreadPool<T>::slock = PTHREAD_MUTEX_INITIALIZER;
#include "SingletonThreadPool.hpp"
#include "Task.hpp"
#include <unistd.h>std::string oper = "+-*/%";int main()
{srand(time(nullptr));SingletonThreadPool<Task>::GetInStance()->ThreadStart();int len = oper.size(); while(true){sleep(1);int data1 = rand() % 10;int data2 = rand() % 10 + 1;char op = oper[rand() % len];Task t(data1, data2, op);SingletonThreadPool<Task>::GetInStance()->push(t);t.GetTask();}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/78505.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78505.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/78505.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Android Compose】焦點管理

官方文檔鏈接&#xff1a; https://developer.android.google.cn/develop/ui/compose/touch-input/focus?hlzh-cn 1、更改焦點遍歷順序 1.1、替換一維遍歷順序 &#xff08;1&#xff09;創建焦點引用對象&#xff1a; /// 創建4個引用對象&#xff08;二選一&#xff09…

dwj2025426

目錄 一、25. K 個一組翻轉鏈表 - 力扣&#xff08;LeetCode&#xff09; 二、 215. 數組中的第K個最大元素 - 力扣&#xff08;LeetCode&#xff09; 三、 15. 三數之和 - 力扣&#xff08;LeetCode&#xff09; 一、25. K 個一組翻轉鏈表 - 力扣&#xff08;LeetCode&#…

C++ std::forward 詳解

在 C 11 引入的眾多特性中&#xff0c;std::forward占據著獨特且重要的地位。它主要用于實現所謂的 “完美轉發”&#xff0c;這一機制在現代 C 編程中發揮著關鍵作用&#xff0c;尤其是在編寫通用庫和高效代碼時。 什么是完美轉發&#xff1f; 完美轉發是指在函數模板中&…

如何保證線程安全(含典型手段與應用場景)

? 1. 什么是線程安全&#xff1f; 線程安全指的是&#xff1a;當多個線程同時訪問同一塊代碼時&#xff0c;無論運行時環境采用怎樣的調度方式或者這些線程將怎樣交替執行&#xff0c;代碼的行為都能正確執行&#xff0c;且不會出現數據不一致、臟數據或異常崩潰。 舉個簡單…

Qt/C++開發監控GB28181系統/協議解釋說明/SIP內容解釋/每一行數據什么含義

一、前言 搞gb28181開發&#xff0c;首要任務就是解析協議&#xff0c;按照gb28181的文檔來&#xff0c;還是非常詳細的&#xff0c;通過抓包工具可以查看到具體的收發數據&#xff0c;也可以打開網絡調試助手工具&#xff0c;監聽5060端口&#xff0c;看到上報的數據&#xf…

C++:string 1

練習題&#xff1a; 這個題的思路是從前往后&#xff0c;從后往前同時找&#xff0c;不是字母的話就繼續&#xff0c;是的話就交換。 代碼&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include <string> using namespace std; //1、4個…

SMT貼片加工費控制與優化實踐指南

內容概要 SMT貼片加工費的控制與優化需建立在對成本結構的系統性認知基礎上。本節從物料采購、設備運行、工藝參數三大維度切入&#xff0c;結合BOM清單管理、鋼網使用規范等實操環節&#xff0c;構建覆蓋全流程的降本增效框架。以下表格列舉了SMT加工成本的典型構成要素及其占…

未來醫院已來:AI如何實現無死角安全監控

AI智慧醫院如何用算法守護安全與效率 ## 背景&#xff1a;醫療場景的智能化轉型需求 現代醫院作為人員密集、場景復雜的公共場所&#xff0c;面臨諸多管理痛點&#xff1a;患者跌倒可能延誤救治、醫鬧事件威脅安全、醫療垃圾處置不當引發感染風險、重點區域&#xff08;如藥…

Nuxt3中使用UnoCSS指南

Nuxt3中使用UnoCSS指南 UnoCSS是一個高度可定制的、原子化CSS引擎&#xff0c;可以輕松集成到Nuxt3項目中。下面介紹如何在Nuxt3中安裝和配置UnoCSS。 安裝步驟 安裝UnoCSS的Nuxt模塊&#xff1a; # 使用pnpm pnpm add -D unocss unocss/nuxt# 使用yarn yarn add -D unocss…

mmap詳解

mmap詳解 mmap基礎概念mmap內存映射原理mmap相關函數調用mmap的使用細節mmap和常規文件操作的區別 mmap基礎概念 mmap是一種內存映射文件的方法&#xff0c;即將一個文件或者其它對象映射到進程的地址空間&#xff0c;實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一…

Vue3的內置組件 -實現過渡動畫 TransitionGroup

Vue3的內置組件 -實現過渡動畫 TransitionGroup 是一個內置組件&#xff0c;用于對 v-for 列表中的元素或組件的插入、移除和順序改變添加動畫效果 支持和 基本相同的 props、CSS 過渡 class 和 JavaScript 鉤子監聽器&#xff0c;但有以下幾點區別&#xff1a; 默認情況下&…

【軟考-架構】14、軟件可靠性基礎

?資料&文章更新? GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目錄 軟件可靠性基本概念軟件可靠性建模軟件可靠性管理軟件可靠性設計N版本程序設計恢復塊設計&#xff08;動態冗余&#xff09;雙機容錯技術、集群技術負載均衡軟件可靠性測試…

使用Python+OpenCV對視頻抽幀保存為JPG圖像

使用PythonOpenCV對視頻抽幀保存為JPG圖像 import os import cv2 import time#視頻文件夾路徑&#xff0c;可修改 videoPath D:\\video\\ #保存的圖片文件夾路徑&#xff0c;可修改 savePath D:\\images\\ videolist os.listdir(videoPath) if not os.path.exists(savePath…

學習整理在centos7上安裝mysql8.0版本教程

學習整理在centos7上安裝mysql8.0版本教程 查看linux系統版本下載mysql數據庫安裝環境檢查解壓mysql安裝包創建MySQL需要的目錄及授權新增用戶組新增組用戶配置mysql環境變量編寫MySQL配置文件初始化數據庫初始化msyql服務啟動mysql修改初始化密碼配置Linux 系統服務工具,使My…

DeepSeek預訓練追求極致的訓練效率的做法

DeepSeek在預訓練階段通過多種技術手段實現了極致的訓練效率,其中包括采用FP8混合精度訓練框架以降低計算和內存需求 ,創新性地引入Multi-head Latent Attention(MLA)壓縮KV緩存以提升推理效率,以及基于Mixture-of-Experts(MoE)的稀疏計算架構以在保證性能的同時顯著降低…

【計算機視覺】CV項目實戰- 深度解析TorchVision_Maskrcnn:基于PyTorch的實例分割實戰指南

深度解析TorchVision_Maskrcnn&#xff1a;基于PyTorch的實例分割實戰指南 技術背景與核心原理Mask R-CNN架構解析項目特點 完整實戰流程環境準備硬件要求軟件依賴 數據準備與標注1. 圖像采集2. 數據標注3. 數據格式轉換 模型構建與訓練1. 模型初始化2. 數據加載器配置3. 訓練優…

x86系列CPU寄存器和匯編指令總結

文章目錄 概要一、寄存器1.1、8086寄存器1.2、通用寄存器1.3、擴展寄存器 二、指令集三、x86指令集常見指令使用說明四、匯編4.1、匯編語法4.2、nsam匯編 五、參考 概要 在對學習Go的過程中&#xff0c;涉及到了匯編&#xff0c;因此對X86系列CPU的背景、寄存器、匯編指令做了一…

戴維斯雙擊選股公式如何編寫?

戴維斯雙擊&#xff0c;指的是營收增長和凈利潤增長同步&#xff0c;并有超預期的財務狀況。 戴維斯雙擊是指在低市盈率&#xff08;P/E&#xff09;時買入股票&#xff0c;待公司盈利增長和市盈率提升后賣出&#xff0c;以獲取雙重收益。以下是一個簡單的通達信選股模型示例&…

前端面試寶典---vue原理

vue的Observer簡化版 class Observer {constructor(value) {if (!value || typeof value ! object) returnthis.walk(value) // 對對象的所有屬性進行遍歷并定義響應式}walk (obj) {Object.keys(obj).forEach(key > defineReactive(obj, key, obj[key]))} } // 定義核心方法…

從“聾啞設備“到超級工廠:EtherCAT轉Modbus協議網關正在重構工業未來

當全球工廠加速邁向工業4.0&#xff0c;您的生產線是否因Modbus設備“拖后腿”而被迫降速&#xff1f;無需百萬改造&#xff01;無需淘汰設備&#xff01;一套EtherCAT從站轉Modbus協議網關&#xff0c;讓30年老機床與智能工廠實時對話&#xff0c;效率飆升300%&#xff01; 一…