(十一)boost庫之多線程間通訊

一、互斥鎖

編程中,引入了對象互斥鎖的概念,來保證共享數據操做的完整性。每一個對象都對應於一個可稱爲" 互斥鎖" 的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象。ios

#include <iostream>
#include <boost/thread.hpp>
using namespace std;
int g_num = 0;
boost::mutex mu;  //定義互斥鎖對象
int Func(int nCount)
{
    for (int i = 0; i < nCount; i++)
    {
        boost::mutex::scoped_lock lock(mu);  //對共享數據進行操做,需加鎖
        g_num++;
        cout << __FUNCTION__ << ": " << g_num << endl;
    }
    return g_num;
}
int _tmain(int argc, _TCHAR* argv[])
{
    boost::thread th1(Func, 100);
    boost::thread th2(Func, 200);
    th1.join();
    th2.join();
    return 0;
}

二、讀寫鎖

boost::shared_mutex rw_mu;   //定義讀寫鎖
int Write(int nCount)
{
    for (int i = 0; i < nCount; i++)
    {
        boost::unique_lock<boost::shared_mutex> lock(rw_mu);   //加惟一鎖
        g_num++;
        cout << __FUNCTION__ << ": " << g_num << endl;
    }
    return g_num;
}
void Read(int nCount)
{
    for (int i = 0; i < nCount; i++)
    {
        boost::shared_lock<boost::shared_mutex> lock(rw_mu);  //加共享鎖
        cout << __FUNCTION__ << ": " << g_num << endl;
    }
}
int _tmain(int argc, _TCHAR* argv[])
{
    boost::thread th1(Write, 100);
    boost::thread th2(Read, 100);
    boost::thread th3(Read, 100);
    th1.join();
    th2.join();
    th3.join();
    return 0;
}

三、條件量

條件量相對於互斥鎖和讀寫鎖來講,並非那麼好理解,簡單點說,條件變量就是用於等待某個條件被觸發,但爲何要配合鎖使用呢,由於咱們的等待不能是乾等,那樣可能會出現死鎖。編程

如線程A負責添加任務到隊列,線程B負責處理隊列中的任務,隊列就是兩個線程的共享資源,使用前必須加鎖,但若是B線程加鎖後,發現隊列中沒有數據,而後等待,A線程準備添加任務時,發現多線程

鎖已經被佔用,因而就無法添加任務,就造成了死鎖。但若是我等待時,釋放鎖資源,A線程就能正常添加任務,完成後通知B線程能夠處理了,那麼整個流程就暢通無阻了,這就是條件量的做用。函數

#include <queue>
boost::mutex g_ioMutex;    //輸出控制鎖
template<typename T>
class CMsgQueue
{
public:
    CMsgQueue(size_t n):m_nCapacity(n)
    {
    }
    void Push(const T& val)
    {
        {
            boost::mutex::scoped_lock lock(m_mu);              //加鎖
            while(m_val.size() == m_nCapacity)                 //隊列已滿
            {
                {
                    boost::mutex::scoped_lock lock(g_ioMutex);
                    cout << "隊列已滿" << endl;
                }
                m_condPush.wait(m_mu);                         //等待,將暫時的解鎖
            }
             m_val.push(val);                                   //添加數據到隊列
        }
        m_condPop.notify_one();                                 //通知讀線程
    }
    void Pop(T& val)
    {
        {
            boost::mutex::scoped_lock lock(m_mu);               //加鎖
            while(m_val.size() == 0)                            //隊列爲空
            {
                {
                    boost::mutex::scoped_lock lock(g_ioMutex);
                    cout << "隊列爲空" << endl;
                }
                m_condPop.wait(m_mu);                           //等待可讀,
            }
            val = m_val.front();                                 //讀取數據
            m_val.pop();
        }
        m_condPush.notify_one();                                 //通知寫線程
    }
private:
    queue<T> m_val;                            //隊列
    int m_nCapacity;                           //隊列最大容量
    boost::condition_variable_any m_condPush;  //寫入條件量
    boost::condition_variable_any m_condPop;   //讀取條件量
    boost::mutex m_mu;                         //互斥鎖
};
CMsgQueue<int> g_numQueue(10);
void FuncA(int nCount)
{
    for (int i = 0; i < nCount; i++)
    {
        {
            boost::mutex::scoped_lock lock(g_ioMutex);
            cout << __FUNCTION__ << " Put " << i << endl;
        }    
        g_numQueue.Push(i);
        
    }
}
void FuncB(int nCount)
{
    for (int i = 0; i < nCount; i++)
    {        
        int val;
        g_numQueue.Pop(val);
        boost::mutex::scoped_lock lock(g_ioMutex);
        cout << __FUNCTION__ << " Get " << val << endl;
    }
}
int _tmain(int argc, _TCHAR* argv[])
{
    boost::thread th1(FuncA, 50);
    boost::thread th2(FuncB, 20);
    boost::thread th3(FuncB, 30);
    th1.join();
    th2.join();
    th3.join();
    return 0;
}

在多線程程序中,鎖的使用須要特別的當心,好比,咱們將FuncA稍微改一下:spa

void FuncA(int nCount)
{
    for (int i = 0; i < nCount; i++)
    {    
        boost::mutex::scoped_lock lock(g_ioMutex);
        cout << __FUNCTION__ << " Put " << i << endl;        
        g_numQueue.Push(i);    
    }
}

若是改爲這樣,程序將陷入死鎖,咱們輕輕鬆鬆就製造了一個死鎖案例。線程

A線程佔用了輸入鎖,那麼B線程的Pop函數將一直在獲取輸入鎖的地方等待,但它已經佔用了m_mu鎖,A線程也就只能一直在等待m_mu,故造成了死鎖。code

相關文章
相關標籤/搜索