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;
}
相關文章
- 手寫執行緒池,對照學習ThreadPoolExecutor執行緒池實現原理!執行緒thread
- C# 使用執行緒池佇列(學習筆記)C#執行緒佇列筆記
- 手寫一個執行緒池,帶你學習ThreadPoolExecutor執行緒池實現原理執行緒thread
- 執行緒池學習執行緒
- 執行緒池的實現執行緒
- 執行緒池之ScheduledThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- 執行緒池之ThreadPoolExecutor執行緒池原始碼分析筆記執行緒thread原始碼筆記
- Java併發程式設計學習筆記----執行緒池Java程式設計筆記執行緒
- SpringBoot執行緒池和Java執行緒池的實現原理Spring Boot執行緒Java
- 物聯網學習教程——執行緒池執行緒
- python執行緒池的實現Python執行緒
- 執行緒池的實現原理執行緒
- Python學習筆記 - 多執行緒Python筆記執行緒
- Linux程式執行緒學習筆記Linux執行緒筆記
- Java 多執行緒學習筆記Java執行緒筆記
- 【java學習】ThreadPoolExecutor 執行緒池Javathread執行緒
- 深入學習執行緒池原理執行緒
- Python學習筆記|Python之執行緒Python筆記執行緒
- Java多執行緒學習筆記(自用)Java執行緒筆記
- 執行緒池ThreadPoolExecutor實現原理執行緒thread
- 簡易執行緒池實現執行緒
- 執行緒池的實現程式碼分析執行緒
- Java多執行緒學習(八)執行緒池與Executor 框架Java執行緒框架
- muduo網路庫Timestamp類
- C# 多執行緒學習筆記 – 1C#執行緒筆記
- python實現自定義執行緒池Python執行緒
- 三、執行緒池知識點整理筆記執行緒筆記
- 十七:主庫的DUMP執行緒(筆記)執行緒筆記
- 執行緒池續:你必須要知道的執行緒池submit()實現原理之FutureTask!執行緒MIT
- Thinking in Java---多執行緒學習筆記(2)ThinkingJava執行緒筆記
- MySQL中介軟體之ProxySQL(5):執行緒、執行緒池、連線池MySql執行緒
- 執行緒池小記錄執行緒
- Java之實現多執行緒的方式三:實現Callable介面(結合執行緒池使用)Java執行緒
- moquette改造筆記(三):優化BrokerInterceptor 中的執行緒池筆記優化執行緒
- 執行緒池的原理與C語言實現執行緒C語言
- 基於C++11的執行緒池實現C++執行緒
- Java執行緒池二:執行緒池原理Java執行緒
- 好程式設計師大資料學習路線分享執行緒學習筆記二程式設計師大資料執行緒筆記
- 好程式設計師大資料學習路線分享多執行緒學習筆記程式設計師大資料執行緒筆記