2011-12-05 81 views
2

我通過並行編程舉例關於種族問題的條件下工作並行編程競爭條件

在這個例子中,他們都表現出了隔離圖案應對競爭條件

爲什麼在這個例子以下是比賽條件不會發生在創建任務時,並且stateObject作爲任務創建的一部分傳遞

我知道我們使用isolatedBalance進行更新......但在我們分配isolatedbalance = (int)stateObject時無法完成其他任務平衡在那裏,即不是0但100

所以,如果有足夠的任務,並且任務調度程序開始了一個早期任務,並且它完成了一個點,當一個稍後的任務被創建並且分配賬戶時。當任務中的一個具有時, finshed的TAKS這是開始

class BankAccount 
{ 
    public int Balance { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 

     var account = new BankAccount(); 
     var tasks = new Task<int>[1000]; 


     for (int i = 0; i < 1000; i++) 
     { 
      tasks[i] = new Task<int>((stateObject)=> 
      { 
       int isobalance = (int) stateObject; 
       for (int j = 0; j < 1000; j++) 
       { 
        isobalance ++; 
       } 
       return isobalance; 
      }, account.Balance); 

      tasks[i].Start(); 
     } 
     Task.WaitAll(tasks); 

     for (int i = 0; i < 1000; i++) 
     { 
      account.Balance += tasks[i].Result; 
     } 

     Console.WriteLine("Epectecd valeu {0}, Counter value {1}",1000000,account.Balance); 

     // wait for input before exiting 
     Console.WriteLine("Press enter to finish"); 
     Console.ReadLine(); 
    } 
} 

回答

4

您已經傳遞給Task構造函數不更新account.Balance它只使用的account.Balance初始值的方法。它不更新它。 int是通過值。從MSDN

值類型變量直接包含其數據,而不是包含對其數據的引用的引用類型變量。因此,將值型變量傳遞給方法意味着將該變量的副本傳遞給方法。對方法內發生的參數所做的任何更改都不會影響存儲在變量中的原始數據。如果您希望被調用的方法更改參數的值,則必須使用ref或out關鍵字通過引用來傳遞它。爲簡單起見,以下示例使用ref。

因此account.Balance在調用Task.WaitAll(tasks);之後纔會更新。 Task.WaitAll()導致代碼在所有任務完成之前停在那裏。只有在此之後,一旦所有結果都被計算出來。將account.Balance更新爲從tasks[i].Result返回的值。

+0

因此,它的關鍵是Task.WaitAll(任務)作爲所有任務被分配account.Balance按值,因爲所有任務強制完成組織結餘將始終爲0時分配account.balance :-) – HoopSnake

1

它不會導致競爭條件,因爲您只複製account.Balance的當前值並將其分配給線程內的局部變量。在創建每個線程時,他們只需複製當前的account.Balance值到他們的堆棧,然後複製到一個局部變量,但沒有線程實際改變它們,它們都在本地副本上工作。想象這就像一個方法調用。當你將一個int傳遞給一個方法時,它被按值複製,然後即使你在方法中修改它,你也不會在外面看到任何改變。

話雖如此,我最喜歡的例子來說明你問的是非常普遍的「爲每個線程分配一個唯一的ID」問題。考慮以下兩種情況:

不是線程安全的:

for(int i = 0; i < n; i++) 
{ 
    Thread t = new Thread(
     o => 
     { 
      int index = i; 
      // do whatever 
     }); 
    t.Start(); 
} 

這不是線程安全的,因爲主線程,而線程都在使用它自己的代碼裏面了我繼續循環。當每個線程t實際開始時,我可能已經達到了n。

線程安全:

for(int i = 0; i < n; i++) 
{ 
    Thread t = new Thread(
     o => 
     { 
      int index = (int)o; 
      // do whatever 
     }); 
    t.Start(i); 
} 

這是線程安全的給我最初的解釋。每個線程在創建時接收到i的當前值,並將其複製到局部變量中,以使線程正確地具有ids 0,1,...,n-1。我希望這個例子更清楚。