2010-07-22 66 views
3

我嘗試在有史以來第一次嵌入多線程,並遇到一些意想不到的問題,希望你能提供幫助。.NET並行處理ArrayList

下面的代碼片段,讓我的煩惱:

ArrayList recordsCollection = new ArrayList(); 
ArrayList batchCollection = null; 
int idx = 0; 

while(true) 
{ 
    // Some code to generate and assign new batchCollection here 
    recordsCollection.Add(batchCollection); 

    ThreadPool.QueueUserWorkItem(delegate 
    { 
    ProcessCollection(recordsCollection.GetRange(idx, 1)); 
    }); 
    Interlocked.Increment(ref idx); 
} 

private void ProcessCollection(ArrayList collection) 
{ 
    // Do some work on collection here 
} 

過程一旦收集方法被調用,我試圖通過收集我得到「的基礎列表的範圍無效」進行迭代。

在此先感謝!

更新:夥計們,謝謝你們每一個人。通過應用您的建議,我能夠極大地簡化並使其發揮作用。

+3

它不會解決你的問題,但你可能要考慮使用'列表'而不是'ArrayList'。 – 2010-07-22 19:23:00

+0

謝謝Mark,我肯定會這麼做的,難怪我沒有找到ArrayList的泛型版本。 :) – 2010-07-22 19:26:08

回答

2

你有幾個問題。

  • 像馬克指出,你正在捕獲一個循環變量,這將真正混淆在這裏的東西。
  • 您正在修改集合,同時在不使用同步機制的情況下讀取它。

我假設你已經省略了代碼用於獲取batchCollection,然後定期從recordsCollection刪除它們爲了簡潔,否則就不會有問題有作爲。

這裏是你如何解決它。

ArrayList recordsCollection = new ArrayList(); 
ArrayList batchCollection = null; 
int idx = 0; 

while(true) 
{ 
    lock (recordsCollection) 
    { 
    recordsCollection.Add(batchCollection); 
    } 

    int capturedIndex = idx; // Used for proper capturing. 

    ThreadPool.QueueUserWorkItem(delegate 
    { 
    ArrayList range; 
    lock (recordsCollection) 
    { 
     range = recordsCollection.GetRange(capturedIndex, 1); 
    } 
    ProcessCollection(range); 
    }); 

    idx++; 
} 

還是我重構後的版本,其中,作爲最好的,我可以告訴反正,做同樣的事情......

List<List<Record>> recordsCollection = new ArrayList(); 
List<Record> batchCollection = null; 

while(true) 
{ 
    recordsCollection.Add(batchCollection); 

    List<List<Record>> range = new List<List<Record>>(); 
    range.Add(batchCollection); 

    ThreadPool.QueueUserWorkItem(delegate 
    { 
    ProcessCollection(range); 
    });  
} 
5

您在這裏使用Interlocked.Increment是不必要的。你希望局部變量idx只能被一個線程看到,所以不需要鎖定。

當前,您正在「關閉循環變量」,這意味着線程可以看到變量的最新值,而不是創建委託時的值。您希望其他線程接收此變量的副本。即使原始變量改變,這些副本也不會改變。

試着改變你的代碼如下:

int j = idx; 
ThreadPool.QueueUserWorkItem(delegate 
{ 
    ProcessCollection(recordsCollection.GetRange(j, 1)); 
}); 

相關問題

相關文章

1

你是在玩火這裏。 你得到了一個開放的封閉,請參閱:http://en.wikipedia.org/wiki/Closure_(computer_science)

此外,爲什麼你使用getRange,如果你只得到一個項目?

使用通用列表也有幫助。

private void wee() 
    { 
     List<List<string>> recordsCollection = new List<List<string>>(); 

     //int idx = 0; 

     while(true) 
     { 
      //scope the batchcollection here if you want to start a thread with an anonymous delegate 
      List<string> batchCollection = null; 
      // Some code to generate and assign new batchCollection here 
      recordsCollection.Add(batchCollection); 

       ThreadPool.QueueUserWorkItem(delegate 
       { 
        ProcessCollection(batchCollection); 
       }); 
       //Interlocked.Increment(ref idx); 
     } 
    } 
    private void ProcessCollection(List<string> collection) 
    { 
     // Do some work on collection here 
    } 

糾正我,如果我錯了,但我不認爲你會需要idx變量了。

而且,不要忘記,例外是通過調用堆棧扔在:http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx

乾杯!