?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???慕斯主頁:修仙—別有洞天
?? ????????????????????????????????????????? ???今日夜電波:マイノリティ脈絡(luò)—ずっと真夜中でいいのに。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0:24━━━━━━???──────── 4:02
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????? ? ?? ? ? ? ?? ? ????
????????????????????????????????????????關(guān)注??點(diǎn)贊??收藏您的每一次鼓勵都是對我莫大的支持??
?
目錄
日志
認(rèn)識ctime
如何遍歷可變參數(shù)列表
日志的實現(xiàn)
線程池
線程池的理解
線程池的實現(xiàn)
使用單例模式改造線程池
單例模式的理解
單例模式的實現(xiàn)
日志
認(rèn)識ctime
? ? ctime
是 C/C++ 中的一個庫,主要用于處理日期和時間。它包含了一系列函數(shù),用于獲取系統(tǒng)當(dāng)前時間、進(jìn)行時間格式化輸出、以及字符串和日期之間的轉(zhuǎn)換等。下面是一些常用的ctime
函數(shù)及其詳解:
????????time_t
????????time_t 是 C/C++ 語言中用于表示時間的一個數(shù)據(jù)類型,通常是一個長整型(long int 或 long long int)。它用來存儲從 1970 年 1 月 1 日 00:00:00(UTC,即協(xié)調(diào)世界時)起至今的秒數(shù),這個起始時間點(diǎn)被稱為 Unix 時間戳的紀(jì)元(epoch)。
- time() 函數(shù)
- 函數(shù)原型:
time_t time(time_t *tloc)
- 功能:返回從紀(jì)元(即格林尼治時間(GMT)1970年1月1日午夜0點(diǎn))開始至今的秒數(shù)。如果
tloc
不是一個空指針,time
函數(shù)還會將返回值寫入tloc
所指向的位置。- 返回值:一個
time_t
類型的值,表示自紀(jì)元以來的秒數(shù)。
- localtime() 函數(shù)
- 函數(shù)原型:
struct tm *localtime(const time_t *timeptr)
- 功能:將
time_t
類型的時間戳轉(zhuǎn)換為本地時間的tm
結(jié)構(gòu)體。- 返回值:一個指向
tm
結(jié)構(gòu)體的指針,包含了轉(zhuǎn)換后的本地時間信息。
- gmtime() 函數(shù)
- 函數(shù)原型:
struct tm *gmtime(const time_t *timeptr)
- 功能:將
time_t
類型的時間戳轉(zhuǎn)換為協(xié)調(diào)世界時(UTC)的tm
結(jié)構(gòu)體。- 返回值:一個指向
tm
結(jié)構(gòu)體的指針,包含了轉(zhuǎn)換后的 UTC 時間信息。
- asctime() 函數(shù)
- 函數(shù)原型:
char *asctime(const struct tm *timeptr)
- 功能:將
tm
日期時間結(jié)構(gòu)體轉(zhuǎn)換成日期時間字符串。- 返回值:指向日期時間字符串的指針。
- ctime() 函數(shù)
- 函數(shù)原型:
char *ctime(const time_t *timeval)
- 功能:將
time_t
秒數(shù)日期時間格式直接轉(zhuǎn)換成日期時間字符串格式輸出。使用更加方便,因為不需要先轉(zhuǎn)換為tm
結(jié)構(gòu)體。- 返回值:指向日期時間字符串的指針。如果返回的時間在1970年1月1日 0:0:0(UTC時間)之前,則返回NULL。
????????struct tm
? ? struct tm
是 C/C++ 語言中的一個結(jié)構(gòu)體,用于表示和處理日期和時間。它的具體結(jié)構(gòu)如下:
struct tm {
int tm_sec; // 秒,范圍從 0 到 59
int tm_min; // 分,范圍從 0 到 59
int tm_hour; // 時,范圍從 0 到 23
int tm_mday; // 一個月中的日,范圍從 1 到 31
int tm_mon; // 月份,范圍從 0 到 11(其中0表示一月,11表示十二月)
int tm_year; // 自1900年起的年數(shù)
int tm_wday; // 一周中的日,范圍從 0(周日)到 6(周六)
int tm_yday; // 一年中的日,范圍從 0 到 365
int tm_isdst; // 夏令時標(biāo)識符,小于0表示沒有夏令時,等于0表示不知道,大于0表示夏令時
};
????????這個結(jié)構(gòu)體包含了年、月、日、時、分、秒等時間信息,使得開發(fā)者能夠方便地獲取和處理當(dāng)前時間、進(jìn)行日期和時間的計算、格式化輸出等操作。
????????在編程中,你可以使用
time()
函數(shù)獲取當(dāng)前時間的時間戳(以秒為單位),然后使用localtime()
或gmtime()
函數(shù)將這個時間戳轉(zhuǎn)換為struct tm
結(jié)構(gòu)體,從而獲取詳細(xì)的日期和時間信息。????????因此如果我們要讓日志獲得具體的時間并且打印出來可以寫出如下函數(shù):
std::string TimeStampExLocalTime()
{
time_t currtime=time(nullptr);
struct tm*curr=localtime(&currtime);
char time_buffer[128];
snprintf(time_buffer,sizeof(time_buffer),"%d-%d-%d %d:%d:%d",curr->tm_year+1900,curr->tm_mon+1,curr->tm_mday,curr->tm_hour,curr->tm_min,curr->tm_sec);
return time_buffer;
}
如何遍歷可變參數(shù)列表
????????在C語言中,遍歷可變參數(shù)列表通常需要使用
va_list
、va_start
、va_arg
和va_end
這四個宏。這些宏在stdarg.h
頭文件中定義,允許你在函數(shù)中處理可變數(shù)量的參數(shù)。????????以下是一個簡單的步驟說明如何遍歷可變參數(shù)列表:
- 定義函數(shù)原型:在函數(shù)原型中使用
...
來表示可變參數(shù)列表。- 初始化
va_list
:在函數(shù)內(nèi)部,使用va_start
宏來初始化一個va_list
類型的變量。va_start
的第一個參數(shù)是你的va_list
變量,第二個參數(shù)是可變參數(shù)列表之前的最后一個固定參數(shù)。- 遍歷參數(shù):使用
va_arg
宏來遍歷參數(shù)列表。每次調(diào)用va_arg
時,它都會返回列表中的下一個參數(shù),并將va_list
指針向前移動到下一個參數(shù)。你需要指定返回參數(shù)的類型。- 結(jié)束遍歷:在遍歷完所有參數(shù)后,使用
va_end
宏來清理va_list
變量。
????????下面是一個示例函數(shù),它接受一個整數(shù)作為參數(shù)數(shù)量,然后遍歷可變參數(shù)列表中的所有整數(shù),并將它們打印出來:
#include <stdarg.h>
#include <stdio.h>
void print_numbers(int count, ...) {
va_list args;
int i;
// 初始化va_list變量
va_start(args, count);
// 遍歷參數(shù)列表
for (i = 0; i < count; i++) {
int number = va_arg(args, int); // 獲取下一個int類型的參數(shù)
printf("%d ", number); // 打印參數(shù)
}
// 清理va_list變量
va_end(args);
printf("\n");
}
int main() {
print_numbers(3, 1, 2, 3); // 輸出: 1 2 3
print_numbers(5, 5, 10, 15, 20, 25); // 輸出: 5 10 15 20 25
return 0;
}
????????在上面的代碼中,
print_numbers
函數(shù)首先使用va_start
初始化va_list
變量args
。然后,它通過一個循環(huán)使用va_arg
宏來遍歷參數(shù)列表,并在每次迭代中打印一個整數(shù)。最后,它使用va_end
來清理va_list
變量。????????請注意,由于C語言在編譯時不會檢查可變參數(shù)的類型或數(shù)量,因此在使用可變參數(shù)時應(yīng)該格外小心,以避免類型不匹配或緩沖區(qū)溢出等問題。此外,確保傳遞給
va_start
的最后一個固定參數(shù)是正確的,因為va_start
使用這個參數(shù)來確定可變參數(shù)列表的起始位置。
日志的實現(xiàn)
#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
enum
{
Debug = 0,
Info,
Warning,
Error,
Fatal
};
enum
{
Screen = 10,
OneFile,
ClassFile
};
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";
}
}
const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";
class Log
{
public:
Log() : style(defaultstyle), filename(default_filename)
{
mkdir(logdir.c_str(), 0775);
}
void Enable(int sty) //
{
style = sty;
}
std::string TimeStampExLocalTime()
{
time_t currtime = time(nullptr);
struct tm *curr = localtime(&currtime);
char time_buffer[128];
snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,
curr->tm_hour, curr->tm_min, curr->tm_sec);
return time_buffer;
}
void WriteLogToOneFile(const std::string &logname, const std::string &message)
{
umask(0);
int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
if(fd < 0) return;
write(fd, message.c_str(), message.size());
close(fd);
// std::ofstream out(logname);
// if (!out.is_open())
// return;
// out.write(message.c_str(), message.size());
// out.close();
}
void WriteLogToClassFile(const std::string &levelstr, const std::string &message)
{
std::string logname = logdir;
logname += "/";
logname += filename;
logname += levelstr;
WriteLogToOneFile(logname, message);
}
void WriteLog(const std::string &levelstr, const std::string &message)
{
switch (style)
{
case Screen:
std::cout << message;
break;
case OneFile:
WriteLogToClassFile("all", message);
break;
case ClassFile:
WriteLogToClassFile(levelstr, message);
break;
default:
break;
}
}
void LogMessage(int level, const char *format, ...) // 類C的一個日志接口
{
char leftbuffer[1024];
std::string levelstr = LevelToString(level);
std::string currtime = TimeStampExLocalTime();
std::string idstr = std::to_string(getpid());
char rightbuffer[1024];
va_list args; // char *, void *
va_start(args, format);
// args 指向了可變參數(shù)部分
vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
va_end(args); // args = nullptr;
snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",
levelstr.c_str(), currtime.c_str(), idstr.c_str());
std::string loginfo = leftbuffer;
loginfo += rightbuffer;
WriteLog(levelstr, loginfo);
}
// void operator()(int level, const char *format, ...)
// {
// LogMessage(int level, const char *format, ...)
// }
~Log() {}
private:
int style;
std::string filename;
};
Log lg;
class Conf
{
public:
Conf()
{
lg.Enable(Screen);
}
~Conf()
{}
};
Conf conf;
線程池
線程池的理解
????????在Linux環(huán)境下,線程池是一種用于管理和復(fù)用線程的技術(shù),旨在避免頻繁地創(chuàng)建和銷毀線程,從而提高系統(tǒng)的性能和資源利用率。線程池預(yù)先創(chuàng)建并維護(hù)一定數(shù)量的線程,這些線程在空閑時處于等待狀態(tài),當(dāng)任務(wù)到來時,線程池會分配一個空閑線程來執(zhí)行任務(wù),任務(wù)完成后線程再次回到空閑狀態(tài),等待下一個任務(wù)的分配。
????????線程池的主要組件包括任務(wù)隊列和線程池管理器。任務(wù)隊列用于存放待執(zhí)行的任務(wù),線程池管理器則負(fù)責(zé)線程的創(chuàng)建、銷毀、調(diào)度和管理。線程池的大小可以根據(jù)實際需求進(jìn)行調(diào)整,以達(dá)到最佳的性能和資源利用率。
????????線程池在Linux下的應(yīng)用場景非常廣泛,尤其適用于需要處理大量并發(fā)任務(wù)或突發(fā)請求的情況。例如,Web服務(wù)器在處理大量用戶請求時,可以使用線程池來管理和復(fù)用線程,提高系統(tǒng)的響應(yīng)速度和吞吐量。此外,線程池還可以用于異步任務(wù)執(zhí)行、定時任務(wù)調(diào)度等場景。
????????總的來說,Linux下的線程池是一種高效、靈活的并發(fā)編程技術(shù),能夠降低系統(tǒng)資源消耗、提高系統(tǒng)性能,并方便對線程并發(fā)數(shù)進(jìn)行管控。通過合理使用線程池,可以顯著提升Linux系統(tǒng)的穩(wěn)定性和可擴(kuò)展性。
線程池的實現(xiàn)
????????實際上線程池的實現(xiàn)同生產(chǎn)者消費(fèi)者模型之間是存在密切的關(guān)系的:
- 線程池為生產(chǎn)者消費(fèi)者模型提供了線程管理和復(fù)用的機(jī)制,使得生產(chǎn)者和消費(fèi)者能夠并發(fā)地執(zhí)行任務(wù),提高了系統(tǒng)的并發(fā)性能。
- 生產(chǎn)者可以將生成的數(shù)據(jù)或任務(wù)提交給線程池,由線程池中的線程負(fù)責(zé)執(zhí)行,實現(xiàn)了任務(wù)的異步處理。我們可以讓多個生產(chǎn)者向線程池推送任務(wù),提高推送任務(wù)的效率。
- 消費(fèi)者可以從線程池中獲取線程來處理從緩沖區(qū)中取出的數(shù)據(jù)或任務(wù),確保了消費(fèi)者的處理速度與生產(chǎn)者的生成速度相匹配,避免了資源的浪費(fèi)??梢院侠淼呐渲镁€程池中的線程來提高線程池的處理效率。
????????線程池實現(xiàn)的大致圖示如下:
?
????????實現(xiàn)如下(詳細(xì)請看代碼,已給出詳細(xì)的注釋,復(fù)用了之前文章的代碼):
#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"
static const int defaultnum = 5;//默認(rèn)的處理任務(wù)的線程數(shù)量
class ThreadData //用于傳輸線程的數(shù)據(jù),這里就先傳輸一個線程名
{
public:
ThreadData(const std::string &name) : threadname(name)
{
}
public:
std::string threadname;
};
template <class T>
class ThreadPool
{
public:
ThreadPool(int thread_num=defaultnum):_thread_num(thread_num) //構(gòu)造線程池,可以指定處理任務(wù)的線程數(shù)量
{
pthread_mutex_init(&_mutex, nullptr); //初始化鎖和條件變量
pthread_cond_init(&_cond, nullptr);
for(int i=0;i<_thread_num;i++) //創(chuàng)建線程,這里我們引用了之前寫的線程接口:Thread(const std::string &threadname, func_t<T> func, T &data)
{
std::string threadname ="thread-"; //構(gòu)建線程名
threadname+=std::to_string(i+1);
ThreadData td(threadname); //線程要傳入的數(shù)據(jù)
_threads.emplace_back(threadname,std::bind(&ThreadPool<T>::ThreadRun,this,std::placeholders::_1),td); //std::vector<Thread<ThreadData>> _threads;利用vector容器將所有的線程都管理起來
// 其中需要特別注意ThreadRun這個成員函數(shù),他是整個線程池中的線程運(yùn)行函數(shù),后續(xù)如何詳見ThreadRun函數(shù)!
lg.LogMessage(Info, "%s is created...\n", threadname.c_str()); //日志信息,lg在日志類中已定義
}
}
public:
bool Start() //啟動線程
{
for(auto &thread : _threads) //循環(huán)將所有線程啟動
{
thread.Start(); //調(diào)用thread類中的啟動線程
lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());
}
return true;
}
void ThreadWait(const ThreadData &td) //根據(jù)條件變量線程等待的封裝
{
lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());
pthread_cond_wait(&_cond,&_mutex);
}
void ThreadWakeup() //喚醒根據(jù)條件變量線程等待的封裝
{
pthread_cond_signal(&_cond);
}
void ThreadRun(ThreadData &td) //真正的線程運(yùn)行的函數(shù),在前面已經(jīng)被_threads容器管理起來了
{
while(true)
{
T t; //任務(wù)的創(chuàng)建用于給對應(yīng)的任務(wù)
{ //先給鎖,保證原子性
LockGuard lockguard(&_mutex);
while(_q.empty()) //任務(wù)隊列中為空則等待
{
ThreadWait(td);
lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());
}
t=_q.front(); //拿取任務(wù)
_q.pop();
}
t(); //重載了() 就是運(yùn)行任務(wù)的意思
lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",
td.threadname.c_str(), t.PrintTask().c_str(), t.PrintResult().c_str());
}
}
void Push(T &in) //給外部將任務(wù)推送進(jìn)隊列的接口
{
lg.LogMessage(Debug, "other thread push a task, task is : %s\n", in.PrintTask().c_str());
LockGuard lockguard(&_mutex);
_q.push(in);
ThreadWakeup(); //推送完成了那么久喚醒正在等待的線程
}
~ThreadPool() //析構(gòu)時自動釋放資源
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
void Wait() //外部用于統(tǒng)一回收線程資源
{
for(auto &thread :_threads)
{
thread.Join();
}
}
private:
std::queue<T> _q; //任務(wù)隊列
std::vector<Thread<ThreadData>> _threads; //線程管理容器
int _thread_num; //線程數(shù)量
pthread_mutex_t _mutex; //互斥鎖
pthread_cond_t _cond; //條件變量
};
????????調(diào)用線程池的順序:
????????1、創(chuàng)建線程池。
????????2、啟動線程池。
????????3、將任務(wù)傳入線程池,線程池會自動處理。
????????如下為一個示例:
#include <iostream>
#include <memory>
#include <ctime>
#include "ThreadPool.hpp"
#include "Task.hpp"
int main()
{
std::unique_ptr<ThreadPool<Task>> tp(new ThreadPool<Task>()); //利用智能指針創(chuàng)建線程池
tp->Start(); //啟動線程池
srand((uint64_t)time(nullptr) ^ getpid());
while (true)
{
int x = rand() % 100 + 1; /->此處開始到——
usleep(1234);
int y = rand() % 200;
usleep(1234);
char oper = opers[rand() % opers.size()];
Task t(x, y, oper);
std::cout << "make task: " << t.PrintTask() << std::endl; /<- 這段只是任務(wù)的前置工作
tp->Push(t); //推送任務(wù)進(jìn)入線程池的任務(wù)隊列處理
sleep(1);
}
tp->Wait(); //回收線程池資源
return 0;
}
實現(xiàn)效果:
?
使用單例模式改造線程池
單例模式的理解
????????單例模式是一種創(chuàng)建型設(shè)計模式,它確保一個類僅有一個實例,并提供一個全局訪問點(diǎn)來訪問這個唯一實例。在軟件設(shè)計中,單例模式用于限制某個類只能創(chuàng)建一個對象。這個類提供了一種訪問其唯一對象的方法,可以直接訪問而不需要實例化該類。
????????單例模式的主要優(yōu)點(diǎn)是:
- 全局唯一實例:由于單例模式限制了類的實例化次數(shù),因此可以確保全局只有一個唯一的實例。這有助于節(jié)省系統(tǒng)資源,避免不必要的重復(fù)創(chuàng)建和銷毀對象。
- 簡化訪問:通過提供一個全局訪問點(diǎn),可以方便地獲取到類的唯一實例,無需每次使用時都進(jìn)行實例化。
- 安全性:在某些場景下,如數(shù)據(jù)庫連接、文件操作等,需要保證只有一個實例進(jìn)行操作,以避免資源沖突或數(shù)據(jù)不一致的問題。單例模式可以確保這種安全性。
????????然而,單例模式也存在一些潛在的問題和缺點(diǎn):
- 擴(kuò)展性問題:由于單例模式限制了類的實例化次數(shù),因此可能不適用于需要多個實例的場景。這可能導(dǎo)致代碼的可擴(kuò)展性受到限制。
- 測試?yán)щy:由于單例模式的唯一實例特性,可能導(dǎo)致在單元測試中難以模擬和替換對象,從而增加了測試的復(fù)雜性。
- 隱藏依賴:使用單例模式的代碼可能隱式地依賴于全局唯一實例的存在,這可能導(dǎo)致代碼之間的耦合度增加,降低代碼的可維護(hù)性。
????????單例模式的分類主要包括以下三種:
- 餓漢式單例:這種單例模式在類被加載的時候,就創(chuàng)建了唯一實例。因此,它天然就是線程安全的。其優(yōu)點(diǎn)在于沒有線程安全的問題,但由于實例在初始化時就已經(jīng)建好,可能會浪費(fèi)一些內(nèi)存空間。
- 懶漢式單例:與餓漢式不同,懶漢式單例在類被加載時并不會創(chuàng)建實例,而是在首次調(diào)用
getInstance()
方法時才進(jìn)行創(chuàng)建。因此,懶漢式單例在延遲加載和節(jié)省內(nèi)存方面有一定的優(yōu)勢。但是,懶漢式單例在實現(xiàn)時需要注意線程安全問題,否則可能會出現(xiàn)多個線程同時創(chuàng)建實例的情況,破壞單例的唯一性。- 登記式單例:這種單例模式是通過將類本身作為一個鍵,將類的唯一實例存儲在某個靜態(tài)的存儲結(jié)構(gòu)中,如Map,以便隨時訪問。這種方式更為靈活,但實現(xiàn)起來相對復(fù)雜一些。
????????在實現(xiàn)單例模式時,需要注意線程安全問題。在多線程環(huán)境下,如果沒有采取適當(dāng)?shù)耐酱胧?,可能會?dǎo)致多個線程同時創(chuàng)建實例,從而破壞單例模式的唯一性。因此,在實現(xiàn)單例模式時,需要確保線程安全,例如通過使用雙重檢查鎖定(double-checked locking)等技術(shù)。(本文會有體現(xiàn))
單例模式的實現(xiàn)
????????本文主要實現(xiàn)的是懶漢式單例,也是根據(jù)上面的線程池進(jìn)行改造而得:
????????我們將構(gòu)造函數(shù)放到了私有但是沒有進(jìn)行改動,改動較大的是我們將拷貝構(gòu)造以及賦值操作給刪除了,這也是為了符合單例模式的特效:全局唯一實例。我們要實現(xiàn)當(dāng)該線程池還沒有被創(chuàng)建出來的時候創(chuàng)建,如果已經(jīng)創(chuàng)建好了那么就返回創(chuàng)建好的指針即可:因此,需要設(shè)置一個最開始就已經(jīng)建立好的指針也就是說要么是全局類型的要么是static的,由于要調(diào)用類內(nèi)的變量,因此我們創(chuàng)建為ThreadPool<T> *ThreadPool<T>::instance;首先設(shè)置為nullptr。接著通過給一個公共的GetInstance()(由于成員變量為static類型,因此函數(shù)也需要為static類型)接口用于外部調(diào)用。在根據(jù)懶漢式的要求首次調(diào)用
getInstance()
方法時才進(jìn)行創(chuàng)建,否則直接返回指針。需要注意這里有多線程訪問的安全問題,因此需要加上一個鎖(由于函數(shù)為static類型,鎖也需要為static類型)。后續(xù)為了線程的效率問題,如果每次調(diào)用都需要加鎖才能調(diào)用,那么效率會很低,因此加上一句 if (instance == nullptr) 判斷是否已經(jīng)創(chuàng)建該單例來節(jié)省操作,直接返回指針即可。如下為具體的實現(xiàn):
#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"
static const int defaultnum = 5; // 默認(rèn)的處理任務(wù)的線程數(shù)量
class ThreadData // 用于傳輸線程的數(shù)據(jù),這里就先傳輸一個線程名
{
public:
ThreadData(const std::string &name) : threadname(name)
{
}
public:
std::string threadname;
};
template <class T>
class ThreadPool
{
private:
ThreadPool(int thread_num = defaultnum) : _thread_num(thread_num) // 構(gòu)造線程池,可以指定處理任務(wù)的線程數(shù)量
{
pthread_mutex_init(&_mutex, nullptr); // 初始化鎖和條件變量
pthread_cond_init(&_cond, nullptr);
for (int i = 0; i < _thread_num; i++) // 創(chuàng)建線程,這里我們引用了之前寫的線程接口:Thread(const std::string &threadname, func_t<T> func, T &data)
{
std::string threadname = "thread-"; // 構(gòu)建線程名
threadname += std::to_string(i + 1);
ThreadData td(threadname); // 線程要傳入的數(shù)據(jù)
_threads.emplace_back(threadname, std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1), td); // std::vector<Thread<ThreadData>> _threads;利用vector容器將所有的線程都管理起來
// 其中需要特別注意ThreadRun這個成員函數(shù),他是整個線程池中的線程運(yùn)行函數(shù),后續(xù)如何詳見ThreadRun函數(shù)!
lg.LogMessage(Info, "%s is created...\n", threadname.c_str()); // 日志信息,lg在日志類中已定義
}
}
ThreadPool(const ThreadPool<T> &tp) = delete;
ThreadPool<T> &operator=(const ThreadPool<T> &cp) = delete;
public:
static ThreadPool<T> *GetInstance()
{
if (instance == nullptr)
{
LockGuard lockguard(&sig_lock);
if (instance == nullptr)
{
lg.LogMessage(Info, "創(chuàng)建單例成功...\n");
instance = new ThreadPool<T>();
}
}
return instance;
}
bool Start() // 啟動線程
{
for (auto &thread : _threads) // 循環(huán)將所有線程啟動
{
thread.Start(); // 調(diào)用thread類中的啟動線程
lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());
}
return true;
}
void ThreadWait(const ThreadData &td) // 根據(jù)條件變量線程等待的封裝
{
lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());
pthread_cond_wait(&_cond, &_mutex);
}
void ThreadWakeup() // 喚醒根據(jù)條件變量線程等待的封裝
{
pthread_cond_signal(&_cond);
}
void ThreadRun(ThreadData &td) // 真正的線程運(yùn)行的函數(shù),在前面已經(jīng)被_threads容器管理起來了
{
while (true)
{
T t; // 任務(wù)的創(chuàng)建用于給對應(yīng)的任務(wù)
{ // 先給鎖,保證原子性
LockGuard lockguard(&_mutex);
while (_q.empty()) // 任務(wù)隊列中為空則等待
{
ThreadWait(td);
lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());
}
t = _q.front(); // 拿取任務(wù)
_q.pop();
}
t(); // 重載了() 就是運(yùn)行任務(wù)的意思
lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",
td.threadname.c_str(), t.PrintTask().c_str(), t.PrintResult().c_str());
}
}
void Push(T &in) // 給外部將任務(wù)推送進(jìn)隊列的接口
{
lg.LogMessage(Debug, "other thread push a task, task is : %s\n", in.PrintTask().c_str());
LockGuard lockguard(&_mutex);
_q.push(in);
ThreadWakeup(); // 推送完成了那么久喚醒正在等待的線程
}
~ThreadPool() // 析構(gòu)時自動釋放資源
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
void Wait() // 外部用于統(tǒng)一回收線程資源
{
for (auto &thread : _threads)
{
thread.Join();
}
}
private:
std::queue<T> _q; // 任務(wù)隊列
std::vector<Thread<ThreadData>> _threads; // 線程管理容器
int _thread_num; // 線程數(shù)量
pthread_mutex_t _mutex; // 互斥鎖
pthread_cond_t _cond; // 條件變量
static ThreadPool<T> *instance;
static pthread_mutex_t sig_lock;
};
template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;
// 擴(kuò)展1:
// int _task_num;
// int _thread_num_low_water; // 3
// int _thread_num_high_water; // 10
// int _task_num_low_water; // 0
// int _task_num_high_water; // 30
?
????????????????????? ? ?感謝你耐心的看到這里?( ′???` )比心,如有哪里有錯誤請?zhí)咭荒_作者o(╥﹏╥)o!?
????????????????????????????????? ? ? ?文章來源:http://www.zghlxwxcb.cn/news/detail-850810.html
????????????????????????????????????????????????????????????????????????給個三連再走嘛~??文章來源地址http://www.zghlxwxcb.cn/news/detail-850810.html
到了這里,關(guān)于Linux下線程池詳解與實現(xiàn):提升多任務(wù)處理效率的關(guān)鍵的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!