.Net 4.0并行库实用性演练
前面說在練習Parallel時,發現另有乾坤,是這樣的代碼:
代碼 static IEnumerable<Person> testFill(){
var list =new List<Person>(9);
Enumerable.Range(1, 99999).ToList().ForEach(n =>
{
var name ="Person"+ n %9;
list.Add(new Person { Id = n, Name = name });
});
Console.WriteLine("Person's count is {0}", list.Count);
return list;
}
static IEnumerable<Person> testFillParallel()
{
var list =new List<Person>(9);
Enumerable.Range(1, 99999).AsParallel().ForAll(n =>
{
var name ="Person"+ n %9;
list.Add(new Person { Id = n, Name = name });
});
Console.WriteLine("Person's count is {0}", list.Count);
return list;
}
class Person
{
internalint Id { get; set; }
internalstring Name { get; set; }
}
試驗結果如下(單位ms):
| ?次數 | ?1 | ?2 | ?3 | ?4 |
| ?Fill 方法 | ?37 | ?27 | ?26 | ?26 |
| ?FillParallel 方法 | ?43 | ?20 | ?19 | ?20 |
這個結果有點奇妙的。第一次多線程居然還不如單線程快,和上文例子比較一下,有點明白了。稍微改了下代碼,在Add語句前加了個Thread.Sleep(1),并把 List<Person>集合元素減為999,試了一次,結果如下(單位ms):
| ?次數 | ?1 | ?2 | ?3 | ?4 |
| ?Fill 方法 | ?1012 | ?998 | ?998 | ?999 |
| ?FillParallel 方法 | ?547 | ?504 | ?504 | ?504 |
多個線程協同工作時,分配任務本身有開銷,要是分配的開銷比任務本身還大,多線程就沒有意義了。就比如你交待別人做某件事,要是交待的功夫比自己做還長,還不如自己做。不過從結果也可以看出一個辯證關系,從長遠打算,第一次讓別人熟悉業務,付出點培訓成本,執行完一次后,以后就輕松多了,速度提高了一倍。如果這里Sleep一下,模擬長一點的單次處理過程,一開始多線程的優勢就會非常明顯。
FillParallel方法,大家覺得有沒有其它問題呢?想必一般人都能看出,這里有最初級的線程安全問題。沒看出的應該是剛學.Net各種集合的初學者,線程安全對他們還只是個太虛幻境。不過借助這個Parallel,就可以輕松神游幻境。把FillParallel方法循環一百次執行,會發現返回結果本來應該有999個元素,輸出的卻顯示卻結果經常少十幾二十個。如果創建List時賦的容量不夠,在List擴容時,還可能引發異常。一般是像下圖這樣(不過一百次都是999也不是不可能,要看你的RP了):
應提醒一點的是,試驗要在Release編譯模式下運行,不然看不到線程安全問題,并行執行的效率提升得也很有限。我用的電腦都是雙核,不知道在單核電腦的運行情況如何,可能有一定區別。
接著我改下邏輯,增加了一個是否Person存在重名的判斷,變成:
代碼 static IEnumerable<Person> testFillParallel(){
var list =new List<Person>(9);
Enumerable.Range(1, 999).AsParallel().ForAll(n =>
{
var name ="Person"+ n %9;
if (list.Count(p => p.Name == name) <1) list.Add(new Person { Id = n, Name = name });
});
Console.WriteLine("Person's count is {0}", list.Count);
return list;
}
RP不管用了,執行幾次,必拋異常:System.InvalidOperationException: Collection was modified; enumeration operation may no execute.
一個線程在枚舉集合元素,這時必須保證集合不被其它線程修改,怎么辦呢?以前,就知道用鎖,現在據說有了線程安全的集合類,在System.Collections.Concurrent命名空間下,有ConcurrentDictionary, ConcurrentQueue, ConcurrentStack,就是沒有ConcurrentList。費了半天,才發現與List對應的應該是BlockingCollection。
把集合定義換成: var list = new BlockingCollection<Person>(9); 只見刷刷刷,哪怕執行幾萬次都可以一路跑完了。
不過這樣做,還是會發現問題,不知大家看出了嗎?
總結
以上是生活随笔為你收集整理的.Net 4.0并行库实用性演练的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 智能化网络管理 为企业信息化保驾护航
- 下一篇: Windows2008+sqlserve