多執行緒問題C++
關於多執行緒,一直都是一知半解,今天又看了些相關知識,總結下:
1.過去寫的微控制器裸跑的程式,其實也屬於多執行緒的,用智慧電錶中的韌體做個比方。
void main(void){
initilize_para(); //基礎引數的初始化
.....
while
{
DealWith485Com(); //485通訊
DealWithEnergy(); //電量資料處理
DealWithIRCOM(); //紅外通訊任務
...... //其他任務
}
}
以上,在while{}迴圈中,所列包括3個“執行緒”處理,執行緒1在執行完之後執行執行緒2,執行緒2執行完進入執行緒3....,後面依次,此類我覺得也可稱之“執行緒”。由於不必存在同時處理的問題,因此不存在爭搶同一共享區域的問題。
2.通常意義上的多執行緒,通過時間片分配各個執行緒的執行時間,表面上看似可以同時執行。通過creat_thread()方式建立執行緒,執行緒執行完畢則退出。存在不同執行緒爭搶共享資源的問題。以C++11實現簡單生產者消費者模式說明。
#include "stdafx.h"
#include <thread>
#include <mutex>
#include <deque>
#include <vector>
#include <condition_variable>
class CThreadDemo
{
private:
std::deque<int> m_data;
std::mutex m_mtx; // 全域性互斥鎖.
std::condition_variable m_cv; // 全域性條件變數.
int m_nGen;
private:
void ProductThread(){
printf("PTr");
while (true){
std::unique_lock <std::mutex> lck(m_mtx);
m_nGen = ++m_nGen % 1000;
printf("product %d\n", m_nGen);
m_data.push_back(m_nGen);
lck.unlock();
m_cv.notify_all();
/* 等待1S */
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
}
}
void ConsumeThread(){
printf("CTr");
while (true){
std::unique_lock <std::mutex> lck(m_mtx);
while (m_data.empty()){
m_cv.wait(lck);
}
int nData = m_data.front();
m_data.pop_front();
printf("consume %d\n", nData);
lck.unlock();
/* 等待2S */
std::chrono::milliseconds dura(2000);
std::this_thread::sleep_for(dura);
}
}
public:
CThreadDemo(){
m_data.clear();
m_nGen = 0;
}
void Start(){
std::vector<std::thread> threads;
threads.clear();
for (int i = 0; i < 5; i++){/* 生產者執行緒 */
threads.push_back(std::thread(&CThreadDemo::ProductThread, this));
}
for (int i = 5; i < 10; i++){/* 消費者執行緒 */
threads.push_back(std::thread(&CThreadDemo::ConsumeThread, this));
}
for (auto& t : threads){/* 等待所有執行緒的退出 */
t.join();
}
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CThreadDemo test;
test.Start();
return 0;
}
實現了一個簡單的生產者-消費者模式,生產者每1S將一個數值放到雙向佇列中,消費者每2S從雙向佇列中取出資料。主要介紹下std::condition_variable的使用。
<condition_variable>是C++標準程式庫中的一個頭檔案,定義了C++11標準中的一些用於併發程式設計時表示條件變數的類與方法等。
條件變數是併發程式設計中的一種控制結構。多個執行緒訪問一個共享資源(或稱臨界區)時,不但需要用互斥鎖實現獨享訪問以避免併發錯誤(稱為競爭危害),在獲得互斥鎖進入臨界區後還需要檢驗特定條件是否成立:
(1)、如果不滿足該條件,擁有互斥鎖的執行緒應該釋放該互斥鎖,把自身阻塞(block)並掛到(suspend)條件變數的執行緒佇列中
(2)、如果滿足該條件,擁有互斥鎖的執行緒在臨界區內訪問共享資源,在退出臨界區時通知(notify)在條件變數的執行緒佇列中處於阻塞狀態的執行緒,被通知的執行緒必須重新申請對該互斥鎖加鎖。
std::condition_variable類的成員函式:
(1)、建構函式:僅支援預設建構函式,拷貝、賦值和移動(move)均是被禁用的。
(2)、wait:當前執行緒呼叫wait()後將被阻塞,直到另外某個執行緒呼叫notify_*喚醒當前執行緒;當執行緒被阻塞時,該函式會自動呼叫std::mutex的unlock()釋放鎖,使得其它被阻塞在鎖競爭上的執行緒得以繼續執行。一旦當前執行緒獲得通知(notify,通常是另外某個執行緒呼叫notify_*喚醒了當前執行緒),wait()函式也是自動呼叫std::mutex的lock()。wait分為無條件被阻塞和帶條件的被阻塞兩種。
無條件被阻塞:呼叫該函式前,當前執行緒應該已經對unique_lock<mutex> lck完成了加鎖。所有使用同一個條件變數的執行緒必須在wait函式中使用同一個unique_lock<mutex>。該wait函式內部會自動呼叫lck.unlock()對互斥鎖解鎖,使得其他被阻塞在互斥鎖上的執行緒恢復執行。使用本函式被阻塞的當前執行緒在獲得通知(notified,通過別的執行緒呼叫 notify_*系列的函式)而被喚醒後,wait()函式恢復執行並自動呼叫lck.lock()對互斥鎖加鎖。
帶條件的被阻塞:wait函式設定了謂詞(Predicate),只有當pred條件為false時呼叫該wait函式才會阻塞當前執行緒,並且在收到其它執行緒的通知後只有當pred為true時才會被解除阻塞。因此,等效於while (!pred()) wait(lck).