1. 程式人生 > >Boost.shared_mutex寫優先,讀共享,交叉互斥

Boost.shared_mutex寫優先,讀共享,交叉互斥

shared_mutex即讀寫鎖,不同與我們常用的獨佔式鎖mutex,shared_mutex是共享與獨佔共存的鎖,實現了讀寫鎖的機制,即多個讀執行緒一個寫執行緒,通常用於對於一個共享區域的讀操作比較頻繁,而寫操作比較少的情況。

    讀寫鎖比起mutex具有更高的適用性,具有更高的並行性,可以有多個執行緒同時佔用讀模式的讀寫鎖,但是隻能有一個執行緒佔用寫模式的讀寫鎖,讀寫鎖的基本規則可以總結為<寫優先,讀共享,交叉互斥>,具體表現為讀寫鎖的三種狀態:
        (1)當讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的執行緒都會被阻塞。(交叉互斥)
        (2)當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的執行緒都可以得到訪問權,但是以寫模式對它進行加鎖的執行緒將會被阻塞。(讀共享,交叉互斥)
        (3)當讀寫鎖在讀模式的鎖狀態時,如果有另外的執行緒試圖以寫模式加鎖,讀寫鎖通常會阻塞隨後的讀模式鎖的請求,這樣可以避免讀模式鎖長期佔用,而等待的寫模式鎖請求則長期阻塞。(寫優先)

注:其實在讀者-寫者問題中,有讀者優先和寫者優先兩種模式,只是在boost中的shared_mutex預設的實現是寫者優先,這其實也是有道理的,因為在我們總是希望讀到的資料是最新的,這就得保證寫者優先。

    下面通過一個 boost::shared_mutex的應用例項來反應其鎖機制,該例子中我們建立兩個讀者執行緒,兩個寫者執行緒,程式的實際執行結果很直接的反應了shared_mutex應用情況。

    #include <boost\ref.hpp>  
    #include <boost\thread\thread.hpp>  
    #include <string>  
    using namespace boost;  
      
    int g_num = 10;      //全域性變數,寫者改變該全域性變數,讀者讀該全域性變數  
    shared_mutex s_mu;   //全域性shared_mutex物件  
      
    //寫執行緒  
    void read_(std::string &str)  
    {  
        const char* c = str.c_str();  
        while (1)  
        {  
            {  
                shared_lock<shared_mutex> m(s_mu);    //讀鎖定,shared_lock  
                printf("執行緒%s進入臨界區,global_num = %d\n", c, g_num);  
                boost::this_thread::sleep(boost::posix_time::seconds(1));    //sleep 1秒,給足夠的時間看其他執行緒是否能進入臨界區  
                printf("執行緒%s離開臨界區...\n", c);  
            }  
            boost::this_thread::sleep(boost::posix_time::seconds(1));   //讀執行緒離開後再slpp 1秒(改變這個值可以看到不一樣的效果)  
        }  
    }  
      
    //讀執行緒  
    void writer_(std::string &str)  
    {  
        const char* c = str.c_str();  
        while (1)  
        {  
            {  
                unique_lock<shared_mutex> m(s_mu);    //寫鎖定,unique_lock  
                ++g_num;  
                printf("執行緒%s進入臨界區,global_num = %d\n", c, g_num);  
                boost::this_thread::sleep(boost::posix_time::seconds(1));    //sleep 1秒,讓其他執行緒有時間進入臨界區  
                printf("執行緒%s離開臨界區...\n", c);  
            }  
            boost::this_thread::sleep(boost::posix_time::seconds(2));   //寫執行緒離開後再slpp 2秒,這裡比讀執行緒多一秒是因為如果這裡也是1秒,那兩個寫執行緒一起請求鎖時會讓讀執行緒飢餓  
        }  
    }  
    int main()  
    {  
        std::string r1 = "read_1";  
        std::string r2 = "read_2";  
        std::string w1 = "writer_1";  
        std::string w2 = "writer_2";  
      
        boost::thread_group tg;  
        tg.create_thread(bind(read_, boost::ref(r1)));   //兩個讀者  
        tg.create_thread(bind(read_, boost::ref(r2)));  
      
        tg.create_thread(bind(writer_, boost::ref(w1)));  //兩個寫者  
        tg.create_thread(bind(writer_, boost::ref(w2)));  
        tg.join_all();  
      
        return 0;  
    }  

從執行結果中可以看出:

如果是讀者獲得了shared_mutex,則其他讀者可以同時進入臨界區;

如果是寫者獲得了shared_mutex,則其他的寫者或讀者都不能進入臨界區,即同時只有一個寫者能進入臨界區。