muduo網路庫學習筆記(5):執行緒池的實現
瞭解生產者-消費者問題
生產者-消費者問題也被稱為有界緩衝區問題,兩個程式/執行緒共享一個公共的固定大小的緩衝區。其中一個是生產者,將資訊放入緩衝區;另一個是消費者,從緩衝區中取出資訊。
問題在於當緩衝區已滿,而此時生產者還想向其中放入一個新的資料項的情況。其解決方法就是讓生產者休眠,待消費者從緩衝區中取出一個或多個資料項時再喚醒它。同樣地,當消費者試圖從緩衝區中取資料而發現緩衝區為空時,消費者就休眠,直到生產者向其中放入一些資料時再將其喚醒。
// 以下程式碼只是對生產者-消費者問題的大致描述
// 因為對count的訪問未加限制,可能會出現競爭條件問題
#define N 100 // 緩衝區的槽數目
int count = 0; // 緩衝區中的資料項的數目
// 生產者
void producer(void)
{
int item;
while(TRUE){
item = produce_item();
if(count == N) sleep();
insert_item(item);
count = count + 1;
if(count == 1) wakeup(consumer);
}
}
// 消費者
void consumer(void)
{
int item;
while(TRUE){
if(count == 0) sleep();
item = remove_item();
count = count - 1;
if(count == N - 1) wakeup(producer);
consume_item(item);
}
}
執行緒池問題本質上也是一個生產者-消費者問題
外部執行緒可以向執行緒池中的任務佇列新增任務,相當於“生產者”;一旦任務佇列中有任務,就喚醒執行緒佇列中的執行緒來執行這些任務,這些執行緒就相當於“消費者”。模型如下圖。
muduo ThreadPool類圖:
(1)任務佇列的實現用到了STL的deque容器
deque容器為一個給定型別的元素進行線性處理,像向量一樣,它能夠快速地隨機訪問任一個元素,並且能夠高效地插入和刪除容器的尾部元素。但它又與vector不同,deque支援高效插入和刪除容器的頭部元素,因此也叫做雙端佇列。
deque常用函式如下:
#include <deque>
void push_front(const T& x); // 雙端佇列頭部增加一個元素x
void push_back(const T& x); // 雙端佇列尾部增加一個元素x
void pop_front(); // 刪除雙端佇列中最前一個元素
void pop_back(); // 刪除雙端佇列中最後一個元素
void clear(); // 清空雙端佇列中最後一個元素
reference at(int pos); // 返回pos位置元素的引用
reference front(); // 返回手元素的引用
reference back(); // 返回尾元素的引用
bool empty() const; // 向量是否為空,若為true,則向量中無元素
(2)幾個成員函式的說明
檔名:ThreadPool.cc
// 啟動執行緒池,啟動的執行緒是固定個數的(numThreads)
void ThreadPool::start(int numThreads)
{
assert(threads_.empty()); // 斷言執行緒池是空的
running_ = true; // 執行狀態標記置為true
threads_.reserve(numThreads); // 為執行緒池預留指定大小的空間
// 建立執行緒
for (int i = 0; i < numThreads; ++i)
{
char id[32];
snprintf(id, sizeof id, "%d", i);
threads_.push_back(new muduo::Thread(
boost::bind(&ThreadPool::runInThread, this), name_+id));
threads_[i].start();
}
}
檔名:ThreadPool.cc
// 關閉執行緒池
void ThreadPool::stop()
{
{
MutexLockGuard lock(mutex_);
running_ = false; // 執行狀態標識置為false
cond_.notifyAll(); // 通知所有執行緒
}
// 等待所有執行緒關閉
// boost::bind呼叫類成員函式時需要傳入類成員函式指標、類物件指標...
for_each(threads_.begin(),
threads_.end(),
boost::bind(&muduo::Thread::join, _1));
}
檔名:ThreadPool.cc
// 執行任務
void ThreadPool::run(const Task& task)
{
// 如果執行緒池沒有執行緒,那麼直接執行任務
// 也就是說假設沒有消費者,那麼生產者直接消費產品,而不把任務加入任務佇列
if (threads_.empty())
{
task();
}
// 如果執行緒池有執行緒,則將任務新增到任務佇列
else
{
MutexLockGuard lock(mutex_);
queue_.push_back(task);
cond_.notify();
}
}
檔名:ThreadPool.cc
// 任務分配函式(獲取任務)
// 執行緒池函式或者執行緒池裡面的函式都可以到這裡取出一個任務
// 然後在自己的執行緒中執行任務,返回一個任務指標
ThreadPool::Task ThreadPool::take()
{
MutexLockGuard lock(mutex_);
// always use a while-loop, due to spurious wakeup(虛假喚醒)
// 任務佇列為空且執行緒池處於執行狀態,需要等待任務的到來
while (queue_.empty() && running_)
{
cond_.wait();
}
Task task;
if(!queue_.empty())
{
// 獲取任務並彈出
task = queue_.front();
queue_.pop_front();
}
return task;
}
相關文章
- muduo網路庫學習筆記(7):執行緒特定資料筆記執行緒
- muduo網路庫學習筆記(6):單例類(執行緒安全的)筆記單例執行緒
- muduo網路庫學習筆記(10):定時器的實現筆記定時器
- java學習筆記.04——執行緒池Java筆記執行緒
- muduo網路庫學習筆記(1):Timestamp類筆記
- muduo網路庫學習筆記(2):原子性操作筆記
- muduo網路庫學習筆記(3):Thread類筆記thread
- muduo網路庫學習筆記(11):有用的runInLoop()函式筆記OOP函式
- muduo網路庫學習筆記(14):chargen服務示例筆記
- muduo網路庫學習筆記(13):TcpConnection生命期的管理筆記TCP
- muduo網路庫學習之EventLoop(二):程式(執行緒)wait/notify 和 EventLoop::runInLoopOOP執行緒AI
- 手寫執行緒池,對照學習ThreadPoolExecutor執行緒池實現原理!執行緒thread
- C# 使用執行緒池佇列(學習筆記)C#執行緒佇列筆記
- muduo網路庫學習筆記(9):Reactor模式的關鍵結構筆記React模式
- muduo網路庫學習筆記(8):高效日誌類的封裝筆記封裝
- muduo網路庫學習筆記(12):TcpServer和TcpConnection類筆記TCPServer
- 執行緒池學習執行緒
- 手寫一個執行緒池,帶你學習ThreadPoolExecutor執行緒池實現原理執行緒thread
- Java併發程式設計學習筆記----執行緒池Java程式設計筆記執行緒
- iOS執行緒學習筆記iOS執行緒筆記
- 多執行緒學習筆記執行緒筆記
- muduo網路庫學習筆記(4):互斥量和條件變數筆記變數
- 物聯網學習教程——執行緒池執行緒
- Java 執行緒池學習Java執行緒
- 執行緒池之ThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒池之ScheduledThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- muduo網路庫學習之muduo_http 庫涉及到的類HTTP
- 執行緒池的實現原理執行緒
- Java 多執行緒學習筆記Java執行緒筆記
- java學習筆記--多執行緒Java筆記執行緒
- Java學習筆記之執行緒Java筆記執行緒
- 多執行緒學習筆記 (轉)執行緒筆記
- muduo網路庫學習之muduo_inspect 庫涉及到的類
- 深入學習執行緒池原理執行緒
- python 學習--執行緒池Python執行緒
- SpringBoot執行緒池和Java執行緒池的實現原理Spring Boot執行緒Java
- Java 多執行緒學習筆記(三)-守護執行緒Java執行緒筆記
- python執行緒池的實現Python執行緒