2014-01-11 86 views
1

我很少想到在兩個連續的表達式之間,函數調用和它的主體的第一個表達式的執行之間,或者在構造函數的調用和它的執行之間發生了什麼初始化。然後,我開始閱讀關於併發...std :: mutex鎖定的順序

1)在連續兩次調用std::thread的構造用相同的可調用(如函數,仿函數,拉姆達),其身體開始有std::lock_guard初始化與同std::mutex對象,標準是否保證第一個thread構造函數調用對應的線程首先執行受鎖定保護的代碼?如果標準沒有作出保證,那麼是否存在任何理論上或實踐上的可能性,對應於第二個構造函數調用的線程首先執行受保護的代碼? (第一thread構造函數調用的初始化或身體的執行期間例如重系統負載)

這裏的一個全局std::mutexm對象和全局unsignednum初始化爲1。功能foo的開啓支架{std::lock_guard之間只有空白。在main中,有兩個std::threadt1t2t1首先調用線程構造函數。 t2第二次調用線程構造函數。每個線程都使用指向foo的指針構造。 t1調用foounsigned參數1t2調用foounsigned參數2。取決於哪個線程先鎖定mutexnum的值將是43,這兩個線程都執行了鎖定保護代碼。 num將等於4如果t1擊敗t2鎖。否則,num將等於3。我在每個循環結束時通過循環並重置num1來運行100,000次試驗。 (據我所知,這樣做的結果,也不應該依賴於哪個線程是join()編第一。)

#include <thread> 
#include <mutex> 
#include <iostream> 

std::mutex m; 
unsigned short num = 1; 

void foo(unsigned short par) { 
    std::lock_guard<std::mutex> guard(m); 
    if (1 == num) 
     num += par; 
    else 
     num *= par; 
} 

int main() { 
    unsigned count = 0; 
    for (unsigned i = 0; i < 100000; ++i) { 
     std::thread t1(foo, 1); 
     std::thread t2(foo, 2); 
     t1.join(); 
     t2.join(); 
     if (4 == num) { 
      ++count; 
     } 
     num = 1; 
    } 
    std::cout << count << std::endl; 
} 

最後,count等於100000,所以原來t1每贏得比賽時間。但這些試驗無法證明任何事情。

3.)標準任務「首先調用thread構造函數」總是暗示「首先調用傳遞給thread構造函數的可調用函數」?

4.)標準任務「首先調用傳遞給thread構造函數的可調用函數」是否始終暗示「首先鎖定mutex」;前提是在可調用函數體內,不存在代碼,該代碼依賴於初始化行之前傳遞給可調用函數的參數嗎? (也排除任何可調用的本地static變量,像所謂的次數的計數器,可用於故意拖延某些呼叫。)

+2

不,是的,不清楚,沒有。 –

回答

3
  1. 沒有,該標準並不能保證第一個線程獲取先鎖定。基本上,如果你需要在線程間施加和排序,你需要在這些線程之間進行同步。即使第一個線程首先調用互斥鎖函數,第二個線程也可能首先獲取鎖。
  2. 絕對。例如,在線程產生時可能只有一個內核可用於您的應用程序,並且如果產生線程在第二個線程產生以等待某些事件後決定,那麼計劃可能會決定處理最新的線程,第二個線程。即使有很多內核可用,但第二個線程速度更快的原因仍然很多。
  3. 不,爲什麼會這樣!第一步是產生一個線程並繼續。到第一個函數對象被調用時,第二個線程可以運行並調用它的函數對象。
  4. 不可以。沒有線程之間的順序保證,除非您明確強制執行它們,因爲它們會破壞併發的目的。