亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Mutex示例/教程?

Mutex示例/教程?

C++ C
慕少森 2019-10-14 10:22:59
我是多線程的新手,并試圖了解互斥鎖的工作原理。做了很多谷歌搜索,我發現了一個不錯的教程,但是它仍然對它的工作方式產生了一些疑問,因為我創建了自己的程序,其中鎖不起作用?;コ饬康囊环N絕對不直觀的語法pthread_mutex_lock( &mutex1 );是,當我真正想要鎖定的是其他變量時,它看起來像互斥量被鎖定了。這種語法是否意味著鎖定互斥鎖會鎖定代碼區域,直到互斥鎖解鎖為止?那么線程如何知道該區域已鎖定?[ 更新:線程知道該區域已被 Memory Fencing 鎖定 ]。難道這種現象不應該稱為臨界區嗎?[ 更新:關鍵部分對象僅在Windows中可用,其中這些對象比互斥對象快,并且僅對實現該對象的線程可見。否則,關鍵部分僅指由互斥體保護的代碼區域 ]簡而言之,能否請您提供最簡單的互斥體示例程序以及有關其工作原理的最簡單的解釋?我相信這將對其他許多新手有所幫助。
查看完整描述

3 回答

?
隔江千里

TA貢獻1906條經驗 獲得超10個贊

盡管互斥鎖可以用于解決其他問題,但它們存在的主要原因是提供相互排斥,從而解決了所謂的競爭條件。當兩個(或多個)線程或進程試圖同時訪問同一變量時,我們就有競爭條件的可能性。考慮以下代碼


//somewhere long ago, we have i declared as int

void my_concurrently_called_function()

{

  i++;

}

該函數的內部看起來很簡單。這只是一個陳述。但是,典型的偽匯編語言等效項可能是:


load i from memory into a register

add 1 to i

store i back into memory

因為在i上執行增量操作都需要使用等效的匯編語言指令,所以我們說對i進行增量運算是一種非大氣操作。原子操作是可以在硬件上完成的操作,保證一旦指令執行開始就不會被中斷。遞增i由3個原子指令鏈組成。在多個線程正在調用該函數的并發系統中,當線程在錯誤的時間讀取或寫入時會出現問題。假設我們有兩個同時運行的線程,一個線程緊接著另一個線程調用該函數。我們還假設我們已將i初始化為0。還假設我們有很多寄存器,并且兩個線程使用的寄存器完全不同,因此不會發生沖突。這些事件的實際時間可能是:


thread 1 load 0 into register from memory corresponding to i //register is currently 0

thread 1 add 1 to a register //register is now 1, but not memory is 0

thread 2 load 0 into register from memory corresponding to i

thread 2 add 1 to a register //register is now 1, but not memory is 0

thread 1 write register to memory //memory is now 1

thread 2 write register to memory //memory is now 1

發生的事情是我們有兩個線程同時遞增i,我們的函數被調用了兩次,但結果與該事實不一致??雌饋碓摵瘮祪H被調用一次。這是因為原子性在計算機級別“中斷”,這意味著線程可以互相中斷或在錯誤的時間一起工作。


我們需要一種機制來解決這個問題。我們需要對以上說明進行一些排序。一種常見的機制是阻止除一個線程外的所有線程。Pthread互斥使用此機制。


任何必須執行一些代碼行的線程(可能會同時不安全地修改其他線程的共享值(使用電話與妻子交談))必須首先獲得互斥鎖。這樣,任何需要訪問共享數據的線程都必須通過互斥鎖。只有這樣,線程才能執行代碼。這部分代碼稱為關鍵部分。


一旦線程執行了關鍵部分,就應該釋放互斥鎖,以便另一個線程可以獲取互斥鎖。


當考慮人類尋求對真實物理對象的專有訪問權時,具有互斥體的概念似乎有些奇怪,但是在編程時,我們必須是故意的。并發線程和流程沒有我們所進行的社會和文化養育,因此我們必須強迫它們很好地共享數據。


因此,從技術上講,互斥鎖是如何工作的?難道它沒有像我們前面提到的那樣遭受同樣的比賽條件嗎?pthread_mutex_lock()難道不是簡單地增加一個變量就復雜嗎?


從技術上講,我們需要一些硬件支持來幫助我們。硬件設計師為我們提供了機器指令,這些指令不僅可以完成一件事,而且必須保證是原子的。這種指令的經典示例是測試設置(TAS)。嘗試獲取資源鎖時,我們可能會使用TAS來檢查內存中的值是否為0。如果是,則表明我們正在使用該資源,并且我們什么也不做(或更準確地說,是,我們會通過某種機制等待。pthreads互斥鎖會將我們放入操作系統的特殊隊列中,并在資源可用時通知我們。Dumber系統可能會要求我們執行緊密的自旋循環,一遍又一遍地測試條件) 。如果內存中的值不為0,則TAS無需使用任何其他指令即可將位置設置為0以外的值。它' 就像將兩個匯編指令合并為1來賦予我們原子性。因此,一旦開始測試和更改值(如果適當的話)就不能中斷。我們可以在這樣的指令之上構建互斥體。


注意:某些部分可能與以前的答案類似。我接受了他的編輯邀請,他更喜歡原來的方式,所以我保留了自己的作品,并注入了一點點措辭。


查看完整回答
反對 回復 2019-10-14
?
慕標5832272

TA貢獻1966條經驗 獲得超4個贊

我最近偶然發現了這篇文章,并認為它需要標準庫的c ++ 11互斥量(即std :: mutex)的更新解決方案。


我在下面粘貼了一些代碼(我使用互斥鎖的第一步-我在W32上通過HANDLE,SetEvent,WaitForMultipleObjects等學習了并發性)。


因為這是我第一次嘗試std :: mutex和朋友,所以我很樂意看到評論,建議和改進!


#include <condition_variable>

#include <mutex>

#include <algorithm>

#include <thread>

#include <queue>

#include <chrono>

#include <iostream>



int _tmain(int argc, _TCHAR* argv[])

{   

    // these vars are shared among the following threads

    std::queue<unsigned int>    nNumbers;


    std::mutex                  mtxQueue;

    std::condition_variable     cvQueue;

    bool                        m_bQueueLocked = false;


    std::mutex                  mtxQuit;

    std::condition_variable     cvQuit;

    bool                        m_bQuit = false;



    std::thread thrQuit(

        [&]()

        {

            using namespace std;            


            this_thread::sleep_for(chrono::seconds(5));


            // set event by setting the bool variable to true

            // then notifying via the condition variable

            m_bQuit = true;

            cvQuit.notify_all();

        }

    );



    std::thread thrProducer(

        [&]()

        {

            using namespace std;


            int nNum = 13;

            unique_lock<mutex> lock( mtxQuit );


            while ( ! m_bQuit )

            {

                while( cvQuit.wait_for( lock, chrono::milliseconds(75) ) == cv_status::timeout )

                {

                    nNum = nNum + 13 / 2;


                    unique_lock<mutex> qLock(mtxQueue);

                    cout << "Produced: " << nNum << "\n";

                    nNumbers.push( nNum );

                }

            }

        }   

    );


    std::thread thrConsumer(

        [&]()

        {

            using namespace std;

            unique_lock<mutex> lock(mtxQuit);


            while( cvQuit.wait_for(lock, chrono::milliseconds(150)) == cv_status::timeout )

            {

                unique_lock<mutex> qLock(mtxQueue);

                if( nNumbers.size() > 0 )

                {

                    cout << "Consumed: " << nNumbers.front() << "\n";

                    nNumbers.pop();

                }               

            }

        }

    );


    thrQuit.join();

    thrProducer.join();

    thrConsumer.join();


    return 0;

}


查看完整回答
反對 回復 2019-10-14
  • 3 回答
  • 0 關注
  • 664 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號