3 回答

TA貢獻(xiàn)1906條經(jīng)驗 獲得超10個贊
盡管互斥鎖可以用于解決其他問題,但它們存在的主要原因是提供相互排斥,從而解決了所謂的競爭條件。當(dāng)兩個(或多個)線程或進(jìn)程試圖同時訪問同一變量時,我們就有競爭條件的可能性??紤]以下代碼
//somewhere long ago, we have i declared as int
void my_concurrently_called_function()
{
i++;
}
該函數(shù)的內(nèi)部看起來很簡單。這只是一個陳述。但是,典型的偽匯編語言等效項可能是:
load i from memory into a register
add 1 to i
store i back into memory
因為在i上執(zhí)行增量操作都需要使用等效的匯編語言指令,所以我們說對i進(jìn)行增量運算是一種非大氣操作。原子操作是可以在硬件上完成的操作,保證一旦指令執(zhí)行開始就不會被中斷。遞增i由3個原子指令鏈組成。在多個線程正在調(diào)用該函數(shù)的并發(fā)系統(tǒng)中,當(dāng)線程在錯誤的時間讀取或?qū)懭霑r會出現(xiàn)問題。假設(shè)我們有兩個同時運行的線程,一個線程緊接著另一個線程調(diào)用該函數(shù)。我們還假設(shè)我們已將i初始化為0。還假設(shè)我們有很多寄存器,并且兩個線程使用的寄存器完全不同,因此不會發(fā)生沖突。這些事件的實際時間可能是:
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
發(fā)生的事情是我們有兩個線程同時遞增i,我們的函數(shù)被調(diào)用了兩次,但結(jié)果與該事實不一致??雌饋碓摵瘮?shù)僅被調(diào)用一次。這是因為原子性在計算機級別“中斷”,這意味著線程可以互相中斷或在錯誤的時間一起工作。
我們需要一種機制來解決這個問題。我們需要對以上說明進(jìn)行一些排序。一種常見的機制是阻止除一個線程外的所有線程。Pthread互斥使用此機制。
任何必須執(zhí)行一些代碼行的線程(可能會同時不安全地修改其他線程的共享值(使用電話與妻子交談))必須首先獲得互斥鎖。這樣,任何需要訪問共享數(shù)據(jù)的線程都必須通過互斥鎖。只有這樣,線程才能執(zhí)行代碼。這部分代碼稱為關(guān)鍵部分。
一旦線程執(zhí)行了關(guān)鍵部分,就應(yīng)該釋放互斥鎖,以便另一個線程可以獲取互斥鎖。
當(dāng)考慮人類尋求對真實物理對象的專有訪問權(quán)時,具有互斥體的概念似乎有些奇怪,但是在編程時,我們必須是故意的。并發(fā)線程和流程沒有我們所進(jìn)行的社會和文化養(yǎng)育,因此我們必須強迫它們很好地共享數(shù)據(jù)。
因此,從技術(shù)上講,互斥鎖是如何工作的?難道它沒有像我們前面提到的那樣遭受同樣的比賽條件嗎?pthread_mutex_lock()難道不是簡單地增加一個變量就復(fù)雜嗎?
從技術(shù)上講,我們需要一些硬件支持來幫助我們。硬件設(shè)計師為我們提供了機器指令,這些指令不僅可以完成一件事,而且必須保證是原子的。這種指令的經(jīng)典示例是測試設(shè)置(TAS)。嘗試獲取資源鎖時,我們可能會使用TAS來檢查內(nèi)存中的值是否為0。如果是,則表明我們正在使用該資源,并且我們什么也不做(或更準(zhǔn)確地說,是,我們會通過某種機制等待。pthreads互斥鎖會將我們放入操作系統(tǒng)的特殊隊列中,并在資源可用時通知我們。Dumber系統(tǒng)可能會要求我們執(zhí)行緊密的自旋循環(huán),一遍又一遍地測試條件) 。如果內(nèi)存中的值不為0,則TAS無需使用任何其他指令即可將位置設(shè)置為0以外的值。它' 就像將兩個匯編指令合并為1來賦予我們原子性。因此,一旦開始測試和更改值(如果適當(dāng)?shù)脑挘┚筒荒苤袛?。我們可以在這樣的指令之上構(gòu)建互斥體。
注意:某些部分可能與以前的答案類似。我接受了他的編輯邀請,他更喜歡原來的方式,所以我保留了自己的作品,并注入了一點點措辭。

TA貢獻(xiàn)1966條經(jīng)驗 獲得超4個贊
我最近偶然發(fā)現(xiàn)了這篇文章,并認(rèn)為它需要標(biāo)準(zhǔn)庫的c ++ 11互斥量(即std :: mutex)的更新解決方案。
我在下面粘貼了一些代碼(我使用互斥鎖的第一步-我在W32上通過HANDLE,SetEvent,WaitForMultipleObjects等學(xué)習(xí)了并發(fā)性)。
因為這是我第一次嘗試std :: mutex和朋友,所以我很樂意看到評論,建議和改進(jìn)!
#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;
}
- 3 回答
- 0 關(guān)注
- 655 瀏覽
添加回答
舉報