2016-07-08 55 views
0

簡單的代碼(我知道,這是一個非常糟糕的一個,但我做到了只是爲例子):多線程。如何平等地分享公共資源?

1 #include <mutex> 
2 #include <iostream> 
3 #include <thread> 
4 
5 std::mutex mu; 
6 
7 void myFunc(void) { 
8   for (int i = 0; i < 100; ++i) { 
9     mu.lock(); 
10     std::cout << "CHILD Thread: " << std::this_thread::get_id() << std::endl; 
11     mu.unlock(); 
12   } 
13 } 
14 
15 int main() 
16 { 
17   std::thread thr(myFunc); 
18   for (int i = 0; i < 100; ++i) { 
19     mu.lock(); 
20     std::cout << "MAIN Thread: " << std::this_thread::get_id() << std::endl; 
21     mu.unlock(); 
22   } 
23   thr.join(); 
24   return 0; 
25 } 

返回此類型的輸出:

1  MAIN Thread: 140581832210240 
2  MAIN Thread: 140581832210240 
3  MAIN Thread: 140581832210240 
4  MAIN Thread: 140581832210240 
5  MAIN Thread: 140581832210240 
6  MAIN Thread: 140581832210240 
7  MAIN Thread: 140581832210240 
8  CHILD Thread: 140581814855424 
9  CHILD Thread: 140581814855424 
10  CHILD Thread: 140581814855424 
11  CHILD Thread: 140581814855424 
12  CHILD Thread: 140581814855424 
13  CHILD Thread: 140581814855424 
14  CHILD Thread: 140581814855424 
15  CHILD Thread: 140581814855424 
16  CHILD Thread: 140581814855424 
17  CHILD Thread: 140581814855424 
18  MAIN Thread: 140581832210240 
19  MAIN Thread: 140581832210240 
20  MAIN Thread: 140581832210240 
21  MAIN Thread: 140581832210240 
22  MAIN Thread: 140581832210240 
23  CHILD Thread: 140581814855424 
24  CHILD Thread: 140581814855424 
25  CHILD Thread: 140581814855424 
26  CHILD Thread: 140581814855424 
27  CHILD Thread: 140581814855424 
28  CHILD Thread: 140581814855424 
29  CHILD Thread: 140581814855424 
      ....... and so on 

,當我看到它 - 這個輸出失敗了多線程的含義,因爲一個線程需要等待很長一段時間。這個輸出應該同時給我兒童,主要的cout,兒童的cout,主要的cout等等。我知道互斥對於共享資源的公平分享不負責任,但是:誰是?我如何將其實施到我的程序中?

謝謝。

編輯:把性病::法院成一個函數:

10 void common_cout(string msg) { 
11   mu.lock(); 
12   std::cout << msg << std::endl; 
13   mu.unlock(); 
14 } 

犯規幫助。

+1

你不應該被所有線程使用相同的內存空間。這就是爲什麼你必須使用鎖等。第一個線程將使用1/number_of_threads內存塊來平均分配空間。在這種情況下,你甚至不需要使用鎖定機制。 還要檢查CPU上可以運行多少個線程。運行比CPU支持更多的線程是沒有意義的。 – nosbor

回答

1

原始代碼在Windows中有相同的問題,但我切換到使用原生Windows等價物,並且此窗口示例以您期望的方式工作,在兩個線程之間交替。 ReleaseMutex()的每個實例都會導致「其他」線程獲取互斥鎖並正在運行。 Main中的Sleep(2)是確保myFunc首先啓動它的簡單方法。

我還創建了一個主線程和兩個線程的總共三個線程的版本。循環順序進行,所以看起來Windows本地互斥鎖是按請求的順序完成的。

對於循環型循環或線程和/或進程之間同步一般,使用每線程或進程一個信號量是更好的,因爲任何的線程或進程可以遞增(釋放/信號)的任何信號。與此相關的問題是信號量不是標準線程接口的本地部分,需要互斥量和條件變量的一些組合來實現等效的信號量。 Windows和posix支持本地信號量。

#include <iostream> 
#include <windows.h> 

static HANDLE mu;      // handle: mutex 
static HANDLE ht1;      // handle: thread 1 
static DWORD id1;      // thread 1 id 

DWORD WINAPI myFunc(LPVOID) { 
    for (int i = 0; i < 20; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "child thread: " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

int main() 
{ 
    mu = CreateMutex(NULL,TRUE,NULL); // main owns mutex 
    ht1 = CreateThread(NULL, 0, myFunc, 0, 0, &id1); 
    Sleep(2);       // make sure myFunc running 
    ReleaseMutex(mu);     // release mutex 
    for (int i = 0; i < 20; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "main thread: " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    WaitForSingleObject(ht1, INFINITE); 
    CloseHandle(ht1); 
    CloseHandle(mu); 
    return 0; 
} 

輸出

child thread: 0 
main thread: 0 
child thread: 1 
main thread: 1 
... 
child thread: 18 
main thread: 18 
child thread: 19 
main thread: 19 

3線程例如:

#include <iostream> 
#include <windows.h> 

static HANDLE mu;      // handle: mutex 
static HANDLE ht0;      // handle: thread 0 
static HANDLE ht1;      // handle: thread 1 
static DWORD id0;      // thread 0 id 
static DWORD id1;      // thread 1 id 

DWORD WINAPI Thread0(LPVOID) { 
    for (int i = 0; i < 10; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "Thread0 : " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

DWORD WINAPI Thread1(LPVOID) { 
    for (int i = 0; i < 10; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "Thread1 : " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

DWORD WINAPI Thread2(LPVOID) { 
    for (int i = 0; i < 10; ++i) { 
     WaitForSingleObject(mu, INFINITE); 
     std::cout << "Thread2 : " << i << std::endl; 
     ReleaseMutex(mu); 
    } 
    return 0; 
} 

int main() 
{ 
    mu = CreateMutex(NULL,TRUE,NULL); // main owns mutex 
    ht0 = CreateThread(NULL, 0, Thread0, 0, 0, &id0); 
    ht1 = CreateThread(NULL, 0, Thread1, 0, 0, &id1); 
    Sleep(2);       // let other threads get started 
    ReleaseMutex(mu);     // release mutex 
    Thread2(0); 
    WaitForSingleObject(ht0, INFINITE); 
    WaitForSingleObject(ht1, INFINITE); 
    CloseHandle(ht0); 
    CloseHandle(ht1); 
    CloseHandle(mu); 
    return 0; 
} 

輸出

Thread0 : 0 
Thread1 : 0 
Thread2 : 0 
Thread0 : 1 
Thread1 : 1 
Thread2 : 1 
... 
Thread0 : 9 
Thread1 : 9 
Thread2 : 9 
+0

對於嚴格確定性的循環機制行爲,根本不會產生線程;只需在一個線程中循環運行代碼,首先調用一個工作函數,然後調用另一個函數。 –

+1

@JeremyFriesner - 雖然在這個例子中沒有演示,但假設將會有重要的代碼與共享資源無關,在這種情況下多線程和/或多處理將會有所幫助。在我工作過的Windows和嵌入式系統的情況下,多線程通信和同步是通過線程間消息傳遞系統完成的,該系統使用消息隊列的鏈表以及每個消息隊列的互斥和信號量。 – rcgldr

1

我知道互斥對於共同資源的公平分享不負責任,但是:誰是?

實際調度是由您的操作系統完成的。

你還沒有說過這是什麼,但它是常見的而不是之間切換線程往往比不必要的,因爲它是低效的(有一些開關的成本)。

換句話說,您的「公平性」的想法 - 大概是每個線程輪流執行的嚴格的循環 - 將是一個昂貴的默認行爲。無論如何,如果它是你想要的,你可以明確地編碼它。公平調度程序的通常目標涉及到可運行線程需要等待多長時間,以及在線程仍在執行(可能是有用的工作)時預先佔用線程的頻率之間的某種平衡。

操作系統的行爲當然也取決於你擁有多少核心。你還沒有提到這個。

...而我如何將它實現到我的程序中?

如果你在你的線程中做了一些合理的實際工作量,你可能會發現你的調度程序的行爲更符合你的喜好。這種人爲測試很少給出有用的結果,特別是因爲您在緊密的循環中執行少量代碼。

+1

如果線程運行在不同的內核上,那麼它們都應該同時運行,不需要任何切換。這個問題可能與編譯器/操作系統如何處理std :: cout有關,或者編譯器/操作系統如何處理鎖定和解鎖。 – rcgldr

+0

這些線程幾乎完成了工作,除了顛簸同一對鎖之外,所以它們不太可能真的會「同時運行」。他們唯一可以並行執行的是for循環增量和測試,而所有的格式化工作都受互斥鎖保護(並且本身可能比解鎖互斥鎖還要便宜) – Useless

+1

睡眠線程在運行時通常會標記爲可運行線程解鎖。然後,在已經運行的線程再次成功鎖定互斥鎖之前,會有一個窗口大約{互斥鎖解鎖系統調用退出路徑,以及大約3個循環指令},以便實際安排睡眠者。你可以看到它不會經常進行,直到正在運行的線程實際執行一些I/O(flush stdout)爲止。 – Useless