C++中線程池ThreadPool源碼解析
什么是線程
線程是進(jìn)程中的?個執(zhí)?單元,負(fù)責(zé)當(dāng)前進(jìn)程中程序的執(zhí)?,?個進(jìn)程中?少有?個線程。?個進(jìn)程中是可以有多個線程的,這個應(yīng)?程序也可以稱之為多線程程序。多線程程序作為一種多任務(wù)、并發(fā)的工作方式
并發(fā)與并?
早期計算機(jī)的 CPU 都是單核的,一個 CPU 在同一時間只能執(zhí)行一個進(jìn)程/線程,當(dāng)系統(tǒng)中有多個進(jìn)程/線程等待執(zhí)行時,CPU 只能執(zhí)行完一個再執(zhí)行下一個。為了提高 CPU 利用率,減少等待時間,人們提出了一種 CPU 并發(fā)工作的理論.
并發(fā):指兩個或多個事件在同?個時間段內(nèi)發(fā)?,當(dāng)系統(tǒng)中有多個進(jìn)程/線程等待執(zhí)行時,CPU只能執(zhí)行完一個再執(zhí)行下一個。
并?:指兩個或多個事件在同?時刻發(fā)?(同時發(fā)?),多核 CPU 的每個核心都可以獨(dú)立地執(zhí)行一個任務(wù),而且多個核心之間不會相互干擾。在不同核心上執(zhí)行的多個任務(wù),是真正地同時運(yùn)行,這種狀態(tài)就叫做并行。。
什么是線程池
顧名思義:線程池就是線程的池子,有很多線程,但是數(shù)量不會超過池子的限制。需要用到多執(zhí)行流進(jìn)行任務(wù)出路的時候,就從池子中取出一個線程去處理,線程池就類似于一個實(shí)現(xiàn)了消費(fèi)者業(yè)務(wù)的生產(chǎn)者與消費(fèi)者模型。
本質(zhì)上:這就是一個基于生產(chǎn)者消費(fèi)者模型來實(shí)現(xiàn)的線程池,那么同樣遵守三種規(guī)則,生產(chǎn)者和生產(chǎn)者之間存在互斥,處理任務(wù)的線程之間存在互斥關(guān)系,生產(chǎn)者和消費(fèi)者之間存在同步和互斥關(guān)系
線程池解決什么問題
線程池維護(hù)者多個線程,等待著分配可并發(fā)執(zhí)行的任務(wù),可以避免在短時間創(chuàng)建和銷毀大量線程帶來時間成本。
總結(jié)為三點(diǎn):
1.避免線程因?yàn)椴幌拗苿?chuàng)建數(shù)量導(dǎo)致的資源耗盡風(fēng)險
2.任務(wù)隊(duì)列緩沖任務(wù),支持忙線不均的作用
3.節(jié)省了大量頻繁創(chuàng)建/銷毀線程的時間成本
怎么用線程池
下面展示一些 threadpool實(shí)現(xiàn),源碼來自openharmony。

/*
* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef NETSTACK_THREAD_POOL
#define NETSTACK_THREAD_POOL
#include <atomic>
#include <condition_variable>
#include <queue>
#include <thread>
#include <vector>
namespace OHOS::NetStack {
template <typename Task, const size_t DEFAULT_THREAD_NUM, const size_t MAX_THREAD_NUM> class ThreadPool {
public:
/**
* disallow default constructor
*/
ThreadPool() = delete;
/**
* disallow copy and move
*/
ThreadPool(const ThreadPool &) = delete;
/**
* disallow copy and move
*/
ThreadPool &operator=(const ThreadPool &) = delete;
/**
* disallow copy and move
*/
ThreadPool(ThreadPool &&) = delete;
/**
* disallow copy and move
*/
ThreadPool &operator=(ThreadPool &&) = delete;
/**
* make DEFAULT_THREAD_NUM threads
* @param timeout if timeout and runningThreadNum_ < DEFAULT_THREAD_NUM, the running thread should be terminated
*/
explicit ThreadPool(uint32_t timeout) : timeout_(timeout), idleThreadNum_(0), needRun_(true)
{
for (int i = 0; i < DEFAULT_THREAD_NUM; ++i) {
std::thread([this] { RunTask(); }).detach();
}
}
/**
* if ~ThreadPool, terminate all thread
*/
~ThreadPool()
{
// set needRun_ = false, and notify all the thread to wake and terminate
needRun_ = false;
while (runningNum_ > 0) {
needRunCondition_.notify_all();
}
}
/**
* push it to taskQueue_ and notify a thread to run it
* @param task new task to Execute
*/
void Push(const Task &task)
{
PushTask(task);
if (runningNum_ < MAX_THREAD_NUM && idleThreadNum_ == 0) {
std::thread([this] { RunTask(); }).detach();
}
needRunCondition_.notify_all();
}
private:
bool IsQueueEmpty()
{
std::lock_guard<std::mutex> guard(mutex_);
return taskQueue_.empty();
}
bool GetTask(Task &task)
{
std::lock_guard<std::mutex> guard(mutex_);
// if taskQueue_ is empty, means timeout
if (taskQueue_.empty()) {
return false;
}
// if run to this line, means that taskQueue_ is not empty
task = taskQueue_.top();
taskQueue_.pop();
return true;
}
void PushTask(const Task &task)
{
std::lock_guard<std::mutex> guard(mutex_);
taskQueue_.push(task);
}
class NumWrapper {
public:
NumWrapper() = delete;
explicit NumWrapper(std::atomic<uint32_t> &num) : num_(num)
{
++num_;
}
~NumWrapper()
{
--num_;
}
private:
std::atomic<uint32_t> &num_;
};
void Sleep()
{
std::mutex needRunMutex;
std::unique_lock<std::mutex> lock(needRunMutex);
/**
* if the thread is waiting, it is idle
* if wake up, this thread is not idle:
* 1 this thread should return
* 2 this thread should run task
* 3 this thread should go to next loop
*/
NumWrapper idleWrapper(idleThreadNum_);
(void)idleWrapper;
needRunCondition_.wait_for(lock, std::chrono::seconds(timeout_),
[this] { return !needRun_ || !IsQueueEmpty(); });
}
void RunTask()
{
NumWrapper runningWrapper(runningNum_);
(void)runningWrapper;
while (needRun_) {
Task task;
if (GetTask(task)) {
task.Execute();
continue;
}
Sleep();
if (!needRun_) {
return;
}
if (GetTask(task)) {
task.Execute();
continue;
}
if (runningNum_ > DEFAULT_THREAD_NUM) {
return;
}
}
}
private:
/**
* other thread put a task to the taskQueue_
*/
std::mutex mutex_;
std::priority_queue<Task> taskQueue_;
/**
* 1 terminate the thread if it is idle for timeout_ seconds
* 2 wait for the thread started util timeout_
* 3 wait for the thread notified util timeout_
* 4 wait for the thread terminated util timeout_
*/
uint32_t timeout_;
/**
* if idleThreadNum_ is zero, make a new thread
*/
std::atomic<uint32_t> idleThreadNum_;
/**
* when ThreadPool object is deleted, wait until runningNum_ is zero.
*/
std::atomic<uint32_t> runningNum_;
/**
* when ThreadPool object is deleted, set needRun_ to false, mean that all thread should be terminated
*/
std::atomic_bool needRun_;
std::condition_variable needRunCondition_;
};
} // namespace OHOS::NetStack
#endif /* NETSTACK_THREAD_POOL */這份源碼的實(shí)現(xiàn),沒有使用一些較難理解的語法,基本上就是使用線程+優(yōu)先級隊(duì)列實(shí)現(xiàn)的。提前創(chuàng)建指定數(shù)目的線程,每次取一個任務(wù)并執(zhí)行。任務(wù)隊(duì)列負(fù)責(zé)存放線程需要處理的任務(wù),工作線程負(fù)責(zé)從任務(wù)隊(duì)列中取出和運(yùn)行任務(wù),可以看成是一個生產(chǎn)者和多個消費(fèi)者的模型。
#include "doctest.h"
DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include <stdexcept>
DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
//#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
//#define DOCTEST_CONFIG_DISABLE
#include <string>
#include <iostream>
#include "thread_pool.h"
//
// Created by Administrator on 2022/8/10.
//
class Task {
public:
Task() = default;
explicit Task(std::string context){
mContext = context;
}
bool operator<(const Task &e) const{
return priority_ < e.priority_;
}
void Execute(){
std::lock_guard<std::mutex> guard(mutex_);
std::cout << "task is execute,name is:"<<mContext<<std::endl;
}
public:
uint32_t priority_;
private:
std::string mContext;
static std::mutex mutex_;
};
#define DEFAULT_THREAD_NUM 3
#define MAX_THREAD_NUM 6
#define TIME_OUT 500
std::mutex Task::mutex_;
static int threadpoolTest(){
static OHOS_NetStack::ThreadPool<Task, DEFAULT_THREAD_NUM, MAX_THREAD_NUM> threadPool_(TIME_OUT);
Task task1("name_1");
Task task2("name_2");
Task task3("name_3");
Task task4("name_4");
threadPool_.Push(task1);
threadPool_.Push(task2);
threadPool_.Push(task3);
threadPool_.Push(task4);
return 0;
}
TEST_CASE("threadPool simple use example, test by doctest unit tool") {
threadpoolTest();
}以上該版本thread_pool的簡單使用示例,可以看到使用稍微麻煩了些。必須定義格式如下的task類,必須實(shí)現(xiàn)operator<和Execute()方法,不過整體實(shí)現(xiàn)還是很不錯的,通俗易懂!
總結(jié)
線程池的應(yīng)用場景:當(dāng)有大量的數(shù)據(jù)請求,需要多執(zhí)行流并發(fā)/并行處理時,可以采用線程池來處理任務(wù),可避免大量線程頻繁創(chuàng)建或銷毀所帶來的時間成本,也可避免在峰值壓力下,系統(tǒng)資源耗盡的風(fēng)險。
到此這篇關(guān)于C++中線程池ThreadPool源碼解析的文章就介紹到這了,更多相關(guān)C++ ThreadPool內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)動態(tài)順序表(vector)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)動態(tài)順序表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-05-05
Qt利用QSortFilterProxyModel代理實(shí)現(xiàn)自定義排序與聯(lián)合過濾
QsortFilterProxyModel類用來為model和view之間提供強(qiáng)大的排序和過濾支持。這篇文章將利用QSortFilterProxyModel代理實(shí)現(xiàn)自定義排序與聯(lián)合過濾,需要的可以參考一下2022-11-11
Qt鍵盤事件實(shí)現(xiàn)圖片在窗口上下左右移動
這篇文章主要為大家詳細(xì)介紹了Qt鍵盤事件實(shí)現(xiàn)圖片在窗口上下左右移動,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08
Qt基礎(chǔ)開發(fā)之Qt多線程類QThread與Qt定時器類QTimer的詳細(xì)方法與實(shí)例
這篇文章主要介紹了Qt基礎(chǔ)開發(fā)之Qt多線程類QThread與Qt定時器類QTimer的詳細(xì)方法與實(shí)例,需要的朋友可以參考下2020-03-03
C++動態(tài)加載so/dll庫的實(shí)現(xiàn)
本文主要介紹了C++動態(tài)加載so/dll庫的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
詳解Qt使用QImage類實(shí)現(xiàn)圖像基本操作
這篇文章主要介紹了Qt如何利用QImage類實(shí)現(xiàn)對圖像的基本操作,包括圖像顯示、圖像縮放、圖像旋轉(zhuǎn)等,感興趣的小伙伴可以跟隨小編一起動手嘗試一下2022-06-06
C++實(shí)現(xiàn)哈夫曼樹簡單創(chuàng)建與遍歷的方法
這篇文章主要介紹了C++實(shí)現(xiàn)哈夫曼樹簡單創(chuàng)建與遍歷的方法,對于C++算法的學(xué)習(xí)來說不失為一個很好的借鑒實(shí)例,需要的朋友可以參考下2014-07-07

