1. 程式人生 > >多執行緒問題C++

多執行緒問題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).