Linux--線程池

目錄

Linux線程池

線程池的概念

線程池的優點

線程池的應用場景

線程池的實現


Linux線程池

線程池的概念

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

其存在的主要原因就為:線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護著多個線程,等待著監督管理者分配可并發執行的任務。

線程池的優點

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

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

線程池的應用場景

線程池常見的應用場景如下:

  1. 需要大量的線程來完成任務,且完成任務的時間比較短。
  2. 對性能要求苛刻的應用,比如要求服務器迅速響應客戶請求。
  3. 接受突發性的大量請求,但不至于使服務器因此產生大量線程的應用。

相關現實場景應用:

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

線程池的實現

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

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

線程池的代碼如下:

.hpp? (ThreadPool.hpp)

#include <iostream>
#include <unistd.h>
#include <queue>
#include <stdlib.h>
#include <pthread.h>class ThreadInfo
{
public:pthread_t tid_;std::string name_;
};static const int defalutnum = 5;template <class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_);}bool IsQueueEmpty(){return tasks_.empty();}std::string GetThreadName(pthread_t tid){for(const auto& ti:threads_){if(ti.tid_ == tid){return ti.name_;}}return "None";}
public:static void *HandlerTask(void *args) // 帶 static 是因為類內的函數第一個參數是默認為 this 指針{ThreadPool<T> *tp = static_cast<ThreadPool*>(args);std::string name = tp->GetThreadName(pthread_self());while(true){tp->Lock();while(tp->IsQueueEmpty()){tp->ThreadSleep();}// 分配到任務T t = tp->Pop();std::cout<< 分配到了一個任務 : " << t << "," << name << "run" << std::endl;tp->Unlock();sleep(1);// 后續的任務處理部分// run();}}void Start(){int num = defalutnum;for(int i=0; i < num; i++){threads_[i].name_ = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid_), nullptr, HandlerTask, this);}}T Pop(){T t = tasks_.front();tasks_.pop();return t;}void Push(const T &t){Lock();tasks_.push(t);Wakeup();Unlock();}
public:ThreadPool(int num = defalutnum) :threads_(num){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}
private:std::vector<ThreadInfo> threads_;std::queue<T> tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;
};

知識回顧:為什么線程池中需要有互斥鎖和條件變量?

因為線程池中的任務隊列是會被多個執行流同時訪問的臨界資源,因此為了避免數據二義性,就會創建互斥鎖與條件變量來進行保護。

線程池當中的線程要從任務隊列里拿任務,前提條件是任務隊列中必須要有任務,因此線程池當中的線程在拿任務之前,需要先判斷任務隊列當中是否有任務,若此時任務隊列為空,那么該線程應該進行等待,直到任務隊列中有任務時再將其喚醒,因此我們需要引入條件變量。

當外部線程向任務隊列中Push一個任務后,此時可能有線程正處于等待狀態,因此在新增任務后需要喚醒在條件變量下等待的線程。

注意:

  • 當某線程被喚醒時,其可能是被異常或是偽喚醒,或者是一些廣播類的喚醒線程操作而導致所有線程被喚醒,使得在被喚醒的若干線程中,只有個別線程能拿到任務。此時應該讓被喚醒的線程再次判斷是否滿足被喚醒條件,所以在判斷任務隊列是否為空時,應該使用while進行判斷,而不是if。
  • 這里的喚醒是用的pthread_cond_signal而不是pthread_cond_broadcast,那是因為,如果外部向隊列中push了一個任務,但我們卻使用pthread_cond_broadcast,將所有的等待線程全部喚醒,但實際情況卻僅需要一個線程,那這肯定會資源的浪費。一瞬間喚醒大量的線程可能會導致系統震蕩,這叫做驚群效應。所以在喚醒線程時最好還是使用pthread_cond_signal函數喚醒一個正在等待的線程即可。
  • 當線程從任務隊列中拿到任務后,該任務就已經屬于當前線程了,與其他線程已經沒有關系了。因此應該在解鎖之后再進行處理任務,而不是在解鎖之前進行。因為處理任務的過程可能會耗費一定的時間,所以我們不要將其放到臨界區當中。
  • 還有就是設計的效率問題,如果我們將處理任務的操作也放在臨界區來處理,那么當某一線程從任務隊列中拿到任務后,其他線程還需要等待該線程將任務處理完后,才有機會進入臨界區。這樣的設計雖然也叫做線程池,但顯然效率是遠沒有僅僅將拿取任務部分放在臨界區的效率高。

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

使用pthread_create函數創建線程時,需要為創建的線程傳入一個HandlerTask(執行例程),該HandlerTask只有一個參數類型為void*的參數,以及返回類型為void*的返回值。

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

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

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

?主函數:.cpp? (proc.cc)

#include <iostream>
#include <ctime>
#include "ThreadPool.hpp"int main()
{std::cout << "process running..." << std::endl;sleep(3);ThreadPool<int> *tp = new ThreadPool<int>();tp->Start();srand(time(nullptr) ^ getpid());while(true){//1. 構建任務int x = rand() % 10 + 1;tp->Push(x);//2. 交給線程池處理std::cout << "proc thread make task: " << std::endl;sleep(1);}
}

?makefile

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

?運行效果如下:

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

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

相關文章

mars3d (基于 Cesium 的輕量化三維地圖庫)

mars3d 是什么? Mars3D 作為基于 Cesium 的輕量化框架,正以其簡潔的 API 和強大的功能重新定義開發體驗。它不僅解決了原生 Cesium 學習曲線陡峭的問題,還通過封裝和優化實現了性能與易用性的雙重突破。無論是智慧城市、低空經濟還是軍事仿真,Mars3D 都能提供高效的三維可視…

uniapp 中使用路由導航守衛,進行登錄鑒權

前言: 在uniapp 使用中,對于登錄界面可能需要路由守衛進行方便判斷跳轉,以下有兩種方案,可以判斷用戶跳轉的時候是否是登錄狀態 方案一: 1. 可以使用插件 hh-router-guard 2. 使用 uni-simpe-route 方案二: 使用通過uni提供的攔截器實現, uni.addInterceptor 1.新建in…

Leetcode 262. 行程和用戶

1.題目基本信息 1.1.題目描述 表&#xff1a;Trips ----------------------- | Column Name | Type | ----------------------- | id | int | | client_id | int | | driver_id | int | | city_id | int | | status | enum | | request_at | varchar | -----------…

P1102 A-B 數對

P1102 A-B 數對 題目背景 出題是一件痛苦的事情! 相同的題目看多了也會有審美疲勞,于是我舍棄了大家所熟悉的 A+B Problem,改用 A-B 了哈哈! 題目描述 給出一串正整數數列以及一個正整數 C C C,要求計算出所有滿足 A ? B = C A - B = C A?B=C 的數對的個數(不同…

devextreme-vue的DxDataGrid如何顯示行號列

devextreme-vue我使用的是23.2版本&#xff0c;其DxDataGrid如何顯示行號列&#xff0c;官方一直沒有方案。 DataGrid - How to display a row number in data rows in Angular | DevExpress Support dxDataGrid - provide capability to display a column with row numbers …

【設計模式06】建造者模式

前言 沒什么用&#xff0c;類似于builder.build UML類圖 代碼示例 package com.sw.learn.pattern.B_create.e_builder;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();for …

datax-web報錯:連接數據庫失敗. 請檢查您的 賬號、密碼、數據庫名稱、IP、Port或者向 DBA 尋求幫助(注意網絡環境)

文章目錄 一、報錯內容二、解決方法 一、報錯內容 背景描述&#xff1a; 在linux安裝了datax202309版本及datax-web2.1.2版本&#xff0c;datax與datax-web默認都是mysql5.x版本的。我的數據庫是mysql8.x版本的。 在datax中執行json腳本從一個mysql導入mysql沒問題&#xff0…

C#調用C++導出的dll怎么調試進入C++ DLL源碼

第一步&#xff1a;首先需要打開C源碼&#xff0c;不需要任何設置&#xff0c;直接下斷點&#xff0c;然后將生成DLL目錄改成到C# exe生成目錄里面 第二步&#xff1a;打開winform項目&#xff0c;然后在C#項目屬性->啟用本地代碼調試勾選后即可 最后在C#下斷點F10或者F11…

Skyeye 云智能制造辦公系統 - Saas v3.16.10 發布

Skyeye 云智能制造&#xff0c;采用 Springboot (微服務) Layui UNI-APP Ant Design Vue 的低代碼平臺。包含 30 多個應用模塊、50 多種電子流程&#xff0c;CRM、PM、ERP、MES、ADM、EHR、筆記、知識庫、項目、門店、商城、財務、多班次考勤、薪資、招聘、云售后、論壇、公…

pdf 合并 python實現(已解決)

在Python中&#xff0c;可以使用多種庫來合并PDF文件&#xff0c;其中最常用的是PyPDF2和PyMuPDF&#xff08;又名fitz&#xff09;。下面我將分別介紹如何使用這兩個庫來合并PDF文件。 使用PyPDF2 首先&#xff0c;你需要安裝PyPDF2。可以使用pip來安裝&#xff1a; 先按照庫…

VCenter SSL過期,登錄提示HTTP 500錯誤解決辦法

報錯圖&#xff1a; 1. 開啟 VCenter ssh遠程連接 登錄vmware esxi&#xff0c;雙擊打開VCenter 控制臺黑窗口&#xff0c;根據提示按F2鍵 兩次&#xff0c;打開系統設置&#xff08;有fn鍵使用fnF2鍵&#xff09; 輸入root密碼&#xff0c;按回車登錄 選擇“Troubleshooting …

Linux 下安裝Oracle 11gR2 x64 netca啟動不了

前言 Oracle Network Configuration Assistant (netca) 是 Oracle 提供的圖形化網絡配置工具&#xff0c;用于簡化 Oracle 數據庫網絡組件的配置和管理。 核心功能 1、配置監聽器 (LISTENER)創建、修改或刪除數據庫監聽器&#xff08;默認端口 1521&#xff09;定義監聽協議…

Pytorch1線性代數實現

Pytorch --線性代數實現 矩陣 正如向量將標量從零階推廣到一階&#xff0c;矩陣將向量從一階推廣到二階。 矩陣&#xff0c;我們通常用粗體、大寫字母來表示 &#xff08;例如&#xff0c;&#x1d44b;、&#x1d44c;和&#x1d44d;&#xff09;&#xff0c; 在代碼中表示…

行業分享丨泛亞汽車數字化轉型實踐:虛擬仿真技術如何賦能汽車研發的創新實踐?

隨著汽車行業向智能化、電動化快速轉型&#xff0c;虛擬仿真技術正成為推動產品研發變革的核心驅動力。作為行業技術先鋒&#xff0c;泛亞汽車通過系統性布局&#xff0c;構建了完整的虛擬仿真技術體系&#xff0c;并總結出三個關鍵方向&#xff1a;打造數字化研發體系、探索精…

【硬核數學】4. AI的“尋路”藝術:優化理論如何找到模型的最優解《從零構建機器學習、深度學習到LLM的數學認知》

歡迎來到本系列的第四篇文章。我們已經知道&#xff0c;訓練一個AI模型&#xff0c;本質上是在尋找一組參數&#xff0c;使得描述模型“有多差”的損失函數 L ( θ ) L(\theta) L(θ) 達到最小值。微積分給了我們強大的工具——梯度下降&#xff0c;告訴我們如何一步步地向著最…

springboot切面編程

SpringBoot切面編程 眾所周知&#xff0c;spring最核心的兩個功能是aop和ioc&#xff0c;即面向切面和控制反轉。本文會講一講SpringBoot如何使用AOP實現面向切面的過程原理。 何為AOP AOP&#xff08;Aspect OrientedProgramming&#xff09;&#xff1a;面向切面編程&…

【Redis#4】Redis 數據結構 -- String類型

一、前言 1. 基本概念 理解&#xff1a;字符串對象是 Redis 中最基本的數據類型,也是我們工作中最常用的數據類型。redis中的鍵都是字符串對象&#xff0c;而且其他幾種數據結構都是在字符串對象基礎上構建的。字符串對象的值實際可以是字符串、數字、甚至是二進制&#xff0…

Spring Boot 集成 Dufs 通過 WebDAV 實現文件管理

Spring Boot 集成 Dufs 通過 WebDAV 實現文件管理 引言 在現代應用開發中&#xff0c;文件存儲和管理是一個常見需求。Dufs 是一個輕量級的文件服務器&#xff0c;支持 WebDAV 協議&#xff0c;可以方便地集成到 Spring Boot 應用中。本文將詳細介紹如何使用 WebDAV 協議在 Sp…

Unity打包時編碼錯誤解決方案:NotSupportedException Encoding 437

問題描述 在Unity項目開發過程中&#xff0c;經常會遇到這樣的情況&#xff1a;項目在編輯器模式下運行完全正常&#xff0c;但是打包后運行時卻出現以下錯誤&#xff1a; NotSupportedException: Encoding 437 data could not be found. Make sure you have correct interna…

Spring Bean的生命周期與作用域詳解

一、Spring Bean的生命周期 Spring Bean的生命周期指的是Bean從創建到銷毀的整個過程。理解這個生命周期對于正確使用Spring框架至關重要&#xff0c;它可以幫助我們在適當的時機執行自定義邏輯。 1. 完整的Bean生命周期階段 Spring Bean的生命周期可以分為以下幾個主要階段…