2010-09-03 150 views
5

我對順序創建的線程的執行順序有問題。 這裏是代碼。boost ::線程執行順序

#include <iostream> 
#include <Windows.h> 
#include <boost/thread.hpp> 

using namespace std; 

boost::mutex mutexA; 
boost::mutex mutexB; 
boost::mutex mutexC; 
boost::mutex mutexD; 


void SomeWork(char letter, int index) 
{ 
    boost::mutex::scoped_lock lock; 
    switch(letter) 
    { 
    case 'A' : lock = boost::mutex::scoped_lock(mutexA); break; 
    case 'B' : lock = boost::mutex::scoped_lock(mutexB); break; 
    case 'C' : lock = boost::mutex::scoped_lock(mutexC); break; 
    case 'D' : lock = boost::mutex::scoped_lock(mutexD); break; 
    } 

    cout << letter <<index << " started" << endl; 
    Sleep(800); 
    cout << letter<<index << " finished" << endl; 
} 

int main(int argc , char * argv[]) 
{ 
    for(int i = 0; i < 16; i++) 
    { 
     char x = rand() % 4 + 65; 
     boost::thread tha = boost::thread(SomeWork,x,i); 
     Sleep(10); 
    } 
Sleep(6000); 
    system("PAUSE"); 
    return 0; 
} 

每次將一個字母(從A到D)和一個genereaion id(i)傳遞給SomeWork作爲線程的方法。我不關心字母之間的執行順序,但是對於特定的字母,如A,Ax必須在Ay之前開始,如果x < y。 代碼的隨機輸出的一個隨機部分是:

 
B0 started 
D1 started 
C2 started 
A3 started 
B0 finished 
B12 started 
D1 finished 
D15 started 
C2 finished 
C6 started 
A3 finished 
A9 started 
B12 finished 
B11 started --> B11 started after B12 finished. 
D15 finished 
D13 started 
C6 finished 
C7 started 
A9 finished 

如何避免這種情況?
謝謝。


我使用條件變量解決了這個問題。但我改變了一些問題。解決方案是跟蹤for循環的索引。所以每個線程知道什麼時候不工作。但就這個代碼而言,我還想問一下另外兩件事情。
首先,在我的電腦上,當我將for-loop索引設置爲350時,我有訪問衝突。 310是循環的數量,這是可以的。所以我意識到有最大數量的線程要生成。我如何確定這個數字?其次,在Visual Studio 2008中,代碼的發佈版本顯示出一種非常奇怪的行爲。如果不使用條件變量(第1行至第3行被註釋掉),那麼線程就會被排序。這怎麼可能發生?

這裏是代碼:

#include <iostream> 
#include <Windows.h> 
#include <boost/thread.hpp> 

using namespace std; 

boost::mutex mutexA; 
boost::mutex mutexB; 
boost::mutex mutexC; 
boost::mutex mutexD; 


class cl 
{ 
public: 
    boost::condition_variable con; 
    boost::mutex mutex_cl; 
    char Letter; 
    int num; 
    cl(char letter) : Letter(letter) , num(0) 
    { 

    } 
    void doWork(int index, int tracknum) 
    { 
     boost::unique_lock<boost::mutex> lock(mutex_cl); 
     while(num != tracknum)  // line 1 
      con.wait(lock); // line 2 
     Sleep(10); 
     num = index; 
     cout << Letter<<index << endl; 
     con.notify_all(); // line 3 
    } 
}; 

int main(int argc , char * argv[]) 
{ 
    cl A('A'); 
    cl B('B'); 
    cl C('C'); 
    cl D('D'); 

    for(int i = 0; i < 100; i++) 
    { 
     boost::thread(&cl::doWork,&A,i+1,i); 
     boost::thread(&cl::doWork,&B,i+1,i); 
     boost::thread(&cl::doWork,&C,i+1,i); 
     boost::thread(&cl::doWork,&D,i+1,i); 
    } 
    cout << "************************************************************************" << endl; 

    Sleep(6000); 
    system("PAUSE"); 
    return 0; 
} 

回答

6

如果你有兩個不同的線程等待鎖,它完全是非確定性的哪一個會獲得一次鎖被先前持有人釋放。我相信這是你正在經歷的。假設B10持有該鎖,同時產生線程B11B12B10釋放鎖 - 無論首先創建哪個線程,或者哪個線程先開始等待,無論是B11還是B12接下來獲取它,都是硬幣折騰。

也許你應該爲每個字母實現工作隊列,這樣你就產生了正好4個線程,每個線程都消耗工作單元?這是以這種方式輕鬆保證訂購的唯一方法。如果多個線程正在等待鎖定,一個簡單的互斥鎖不會保證排序。

+0

你的工作單位是什麼意思? – 2010-09-03 13:30:10

+0

線程完成一部分工作的一些表示。在這種情況下,一個工作單位基本上是一個「(字母,索引)」對。 – Gian 2010-09-03 13:48:34

2

儘管B11在B12之前啓動,但不能保證給定一個CPU時間片來執行B12之前的SomeWork()。這個決定取決於操作系統及其調度程序。

互斥鎖通常用於同步線程之間的數據訪問,並且已經通過線程執行序列(即數據訪問)提出了一個問題。

如果組「A」的線程在相同的數據上執行相同的代碼,那麼就使用一個線程。這將消除組中線程之間的上下文切換併產生相同的結果。如果數據正在改變,則考慮生產者/消費者模式。 Paul Bridger給出了一個簡單易懂的生產者/消費者例子here

1

你的線程有開始執行前必須滿足的依賴關係。在你的例子中,B12依賴於B0和B11。不知何故,你必須跟蹤依賴性知識。未完成依賴的線程必須等待。我想看看condition variables。每次線程完成SomeWork()時,它都會使用條件變量的notify_all()方法。然後所有等待的線程必須檢查它們是否仍然有依賴關係。如果是這樣,請回去等待。否則,請繼續並調用SomeWork()。

您需要一些方法來確定每個線程是否具有未完成的依賴關係。這可能是一些全球可用的實體。你只應該修改它,當你有互斥(在SomeWork())。對於簡單的數據結構,多線程讀取應該是安全的。