C#并發(fā)容器之ConcurrentDictionary與普通Dictionary帶鎖性能詳解
結(jié)果已經(jīng)寫在注釋中
static void Main(string[] args) { var concurrentDictionary = new ConcurrentDictionary<int, string>(); var dictionary = new Dictionary<int, string>(); var sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 1000000; i++) { lock (dictionary) { dictionary[i] = Item; } } sw.Stop(); Console.WriteLine("wrinting to dictionary with a lock: {0}", sw.Elapsed); //wrinting to dictionary with a lock: 00:00:00.0633939 sw.Restart(); for (int i = 0; i < 1000000; i++) { concurrentDictionary[i] = Item; } sw.Stop(); Console.WriteLine("wrinting to a concurrent dictionary: {0}", sw.Elapsed); //wrinting to a concurrent dictionary: 00:00:00.2889851 //對(duì)于寫入操作并發(fā)詞典要比普通帶鎖詞典要慢 sw.Restart(); for (int i = 0; i < 1000000; i++) { lock (dictionary) { CurrentItem = dictionary[i]; } } sw.Stop(); Console.WriteLine("reading from dictionary with a lock: {0}", sw.Elapsed); //reading from dictionary with a lock: 00:00:00.0286066 sw.Restart(); for (int i = 0; i < 1000000; i++) { CurrentItem = concurrentDictionary[i]; } sw.Stop(); Console.WriteLine("reading from a concurrent dictionary: {0}", sw.Elapsed); //reading from a concurrent dictionary: 00:00:00.0196372 //對(duì)于讀取操作并發(fā)詞典要比普通帶鎖詞典要快 //concurrentDictionary采用細(xì)粒度鎖定[fine-grained locking] //普通帶鎖dictionary采用粗粒度鎖定[coarse-grained locking] //在多核多線程的情況下concurrentDictionary將有更好的性能表現(xiàn) sw.Restart(); Console.ReadKey(); } const string Item = "Dictionary item"; public static string CurrentItem;
補(bǔ)充:C#中普通字典(Dictionary)、并發(fā)字典(ConcurrentDictionary)、和哈希表(Hashtable)讀寫性能比較
一、說明
程序有時(shí)候需要并發(fā)多線程操作,多線程讀取同一個(gè)容器內(nèi)的東西是可以的,但是如果需要修改及寫入到同一容器內(nèi),會(huì)有索引失敗的問題,即兩個(gè)進(jìn)程同時(shí)向同一個(gè)位置寫入內(nèi)容,這種情況下需要通過lock(var),將容器鎖定,也可以直接使用可并發(fā)讀寫的容器(ConcurrentDictionary)
測(cè)試分2部分,一次是寫入操作,包含帶鎖寫入和不帶鎖寫入,其中每個(gè)里面又細(xì)分為寫入字符串和寫入一個(gè)類,還有一次是遍歷操作,同樣包含帶鎖讀和不帶鎖讀,其中也分為讀取字符串和讀取類。
二、測(cè)試結(jié)果
2.1、寫入用時(shí)
2.2、遍歷用時(shí)
2.3、結(jié)論
對(duì)于寫入操作速度:普通詞典 > HashTable > 并發(fā)詞典
對(duì)于讀操作速度:并發(fā)字典 > 帶鎖字典 > HashTable
無(wú)論普通字典還是HashTable,帶鎖花費(fèi)的時(shí)間都要比不帶鎖慢,為了線程安全,肯定要犧牲時(shí)間的。
所以如果需要自己寫入的話,推薦帶鎖普通字典,讀寫速度都很均衡。
三、測(cè)試代碼如下
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace BaseMultiThread { class Program { static void Main(string[] args) { ConcurrentDictionary<int, string> _CctDic= new ConcurrentDictionary<int, string>(); ConcurrentDictionary<int, Student> _CctDicClass = new ConcurrentDictionary<int, Student>(); Dictionary<int, string> _Dic = new Dictionary<int, string>(); Dictionary<int, Student> _DicClass = new Dictionary<int, Student>(); Hashtable _Ht = new Hashtable(); Hashtable _HtClass = new Hashtable(); string _CurrentItem = ""; const string _Item = "字符串"; const int _NUM = 10000000;//執(zhí)行次數(shù) Student _CurrentStudent = null; Student student = new Student { Name = _Item, Age = 23 }; Stopwatch _SW = new Stopwatch(); //字符串寫入字典(無(wú)鎖) _SW.Start(); for (int i = 0; i < _NUM; i++) { _Dic[i] = _Item; } _SW.Stop(); Console.WriteLine("向字典寫入【字符串】不添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //字符串寫入字典(有鎖) _Dic = new Dictionary<int, string>(); _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_Dic) { _Dic[i] = _Item; } } _SW.Stop(); Console.WriteLine("向字典寫入【字符串】添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //類寫入字典(無(wú)鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _DicClass[i] = student; } _SW.Stop(); Console.WriteLine("向子典寫入【學(xué)生類】不添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //類寫入字典(有鎖) _DicClass = new Dictionary<int, Student>(); _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_DicClass) { _DicClass[i] = student; } } _SW.Stop(); Console.WriteLine("向子典寫入【學(xué)生類】添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); Console.WriteLine("----------------------------------------------------"); //字符串寫入HashTable(無(wú)鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _Ht[i] = _Item; } _SW.Stop(); Console.WriteLine("向HashTable寫入【字符串】不添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //字符串寫入HashTable(有鎖) _Ht = new Hashtable(); _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_Ht) { _Ht[i] = _Item; } } _SW.Stop(); Console.WriteLine("向HashTable寫入【字符串】添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //類寫入HashTable(無(wú)鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _HtClass[i] = student; } _SW.Stop(); Console.WriteLine("向HashTable寫入【學(xué)生類】不添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //類寫入HashTable(有鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_HtClass) { _HtClass[i] = student; } } _SW.Stop(); Console.WriteLine("向HashTable寫入【學(xué)生類】添加鎖(Lock)花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); Console.WriteLine("----------------------------------------------------------"); //字符串寫入ConcurrentDictionary _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CctDic[i] = _Item; } _SW.Stop(); Console.WriteLine("向ConcurrentDictionary寫入【字符串】 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //類寫入ConcurrentDictionary _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CctDicClass[i] = student; } _SW.Stop(); Console.WriteLine("向ConcurrentDictionary寫入【學(xué)生類】 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); Console.WriteLine("--------------------------------------------------------"); //遍歷普通字典(無(wú)鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CurrentItem = _Dic[i]; } _SW.Stop(); Console.WriteLine("遍歷【普通】字典(無(wú)鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //遍歷普通字典(有鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_Dic) { _CurrentItem = _Dic[i]; } } _SW.Stop(); Console.WriteLine("遍歷【普通】字典(有鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //遍歷類字典(無(wú)鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CurrentStudent = _DicClass[i]; } _SW.Stop(); Console.WriteLine("遍歷【學(xué)生類】字典(無(wú)鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //遍歷類字典(有鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_Dic) { _CurrentStudent = _DicClass[i]; } } _SW.Stop(); Console.WriteLine("遍歷【學(xué)生類】字典(有鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); Console.WriteLine("--------------------------------------------------------"); //遍歷HashTable(無(wú)鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CurrentItem = _Ht[i].ToString(); } _SW.Stop(); Console.WriteLine("遍歷【HashTable】字典(無(wú)鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //遍歷HashTable(有鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_Dic) { _CurrentItem = _Ht[i].ToString(); } } _SW.Stop(); Console.WriteLine("遍歷【HashTable】字典(有鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //遍歷HashTable類(無(wú)鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CurrentStudent = (Student)_HtClass[i]; } _SW.Stop(); Console.WriteLine("遍歷【HashTable學(xué)生類】字典(無(wú)鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //遍歷HashTable類(有鎖) _SW.Restart(); for (int i = 0; i < _NUM; i++) { lock (_Dic) { _CurrentStudent = (Student)_HtClass[i]; } } _SW.Stop(); Console.WriteLine("遍歷【HashTable學(xué)生類】字典(有鎖) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); Console.WriteLine("--------------------------------------------------------"); //遍歷ConCurrent字典 _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CurrentItem = _CctDic[i]; } _SW.Stop(); Console.WriteLine("遍歷【ConCurrent字典】(字符串) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); //遍歷ConCurrent字典(類) _SW.Restart(); for (int i = 0; i < _NUM; i++) { _CurrentStudent = _CctDicClass[i]; } _SW.Stop(); Console.WriteLine("遍歷【ConCurrent字典】(學(xué)生類) 花費(fèi)時(shí)間為:{0} 毫秒", _SW.Elapsed.TotalMilliseconds); Console.WriteLine("--------------------------------------------------------"); _SW.Restart(); Console.WriteLine("-------------------結(jié)束---------------------------"); Console.ReadLine(); } }//Class_end public class Student { public string Name; public int Age; } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
C#使用IComparer自定義List類實(shí)現(xiàn)排序的方法
這篇文章主要介紹了C#使用IComparer自定義List類實(shí)現(xiàn)排序的方法,涉及C#使用IComparer接口定義List類進(jìn)行排序的相關(guān)技巧,需要的朋友可以參考下2015-08-08深入多線程之:深入生產(chǎn)者、消費(fèi)者隊(duì)列分析
本篇文章是對(duì)生產(chǎn)者與消費(fèi)者隊(duì)列進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C#滑動(dòng)驗(yàn)證碼拼圖驗(yàn)證功能實(shí)現(xiàn)(SlideCaptcha)
目前網(wǎng)站上的驗(yàn)證碼機(jī)制可謂是五花八門,有簡(jiǎn)單的數(shù)字驗(yàn)證,有摻雜了字母和文字的混淆驗(yàn)證,還有通過滑塊進(jìn)行的拼圖驗(yàn)證,下面這篇文章主要給大家介紹了關(guān)于C#滑動(dòng)驗(yàn)證碼拼圖驗(yàn)證功能的實(shí)現(xiàn)方法,需要的朋友可以參考下2022-04-04.net實(shí)現(xiàn)裁剪網(wǎng)站上傳圖片的方法
這篇文章主要介紹了.net實(shí)現(xiàn)裁剪網(wǎng)站上傳圖片的方法,比較實(shí)用的功能,需要的朋友可以參考下2014-07-07c#使用linq把多列的List轉(zhuǎn)化為只有指定列的List
這篇文章主要介紹了c#使用linq把多列的List轉(zhuǎn)化為只有指定列的List,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12