一、說明
程序有時候需要併發多線程操作,多線程讀取同一個容器內的東西是可以的,但是如果需要修改及寫入到同一容器內,會有索引失敗的問題,即兩個進程同時向同一個位置寫入內容,這種情況下需要通過lock(var),將容器鎖定,也可以直接使用可併發讀寫的容器(ConcurrentDictionary)
測試分2部分,一次是寫入操作,包含帶鎖寫入和不帶鎖寫入,其中每個裏面又細分爲寫入字符串和寫入一個類,還有一次是遍歷操作,同樣包含帶鎖讀和不帶鎖讀,其中也分爲讀取字符串和讀取類。
二、測試結果
2.1、寫入用時
2.2、遍歷用時
2.3、結論
- 對於寫入操作速度:普通詞典 > HashTable > 併發詞典
- 對於讀操作速度:併發字典 > 帶鎖字典 > HashTable
- 無論普通字典還是HashTable,帶鎖花費的時間都要比不帶鎖慢,爲了線程安全,肯定要犧牲時間的。
所以如果需要自己寫入的話,推薦帶鎖普通字典,讀寫速度都很均衡。
三、測試代碼如下
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;//執行次數
Student _CurrentStudent = null;
Student student = new Student { Name = _Item, Age = 23 };
Stopwatch _SW = new Stopwatch();
//字符串寫入字典(無鎖)
_SW.Start();
for (int i = 0; i < _NUM; i++)
{
_Dic[i] = _Item;
}
_SW.Stop();
Console.WriteLine("向字典寫入【字符串】不添加鎖(Lock)花費時間爲:{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)花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//類寫入字典(無鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_DicClass[i] = student;
}
_SW.Stop();
Console.WriteLine("向子典寫入【學生類】不添加鎖(Lock)花費時間爲:{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("向子典寫入【學生類】添加鎖(Lock)花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
Console.WriteLine("----------------------------------------------------");
//字符串寫入HashTable(無鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_Ht[i] = _Item;
}
_SW.Stop();
Console.WriteLine("向HashTable寫入【字符串】不添加鎖(Lock)花費時間爲:{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)花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//類寫入HashTable(無鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_HtClass[i] = student;
}
_SW.Stop();
Console.WriteLine("向HashTable寫入【學生類】不添加鎖(Lock)花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//類寫入HashTable(有鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
lock (_HtClass)
{
_HtClass[i] = student;
}
}
_SW.Stop();
Console.WriteLine("向HashTable寫入【學生類】添加鎖(Lock)花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
Console.WriteLine("----------------------------------------------------------");
//字符串寫入ConcurrentDictionary
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CctDic[i] = _Item;
}
_SW.Stop();
Console.WriteLine("向ConcurrentDictionary寫入【字符串】 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//類寫入ConcurrentDictionary
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CctDicClass[i] = student;
}
_SW.Stop();
Console.WriteLine("向ConcurrentDictionary寫入【學生類】 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
Console.WriteLine("--------------------------------------------------------");
//遍歷普通字典(無鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CurrentItem = _Dic[i];
}
_SW.Stop();
Console.WriteLine("遍歷【普通】字典(無鎖) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//遍歷普通字典(有鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
lock (_Dic)
{
_CurrentItem = _Dic[i];
}
}
_SW.Stop();
Console.WriteLine("遍歷【普通】字典(有鎖) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//遍歷類字典(無鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CurrentStudent = _DicClass[i];
}
_SW.Stop();
Console.WriteLine("遍歷【學生類】字典(無鎖) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//遍歷類字典(有鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
lock (_Dic)
{
_CurrentStudent = _DicClass[i];
}
}
_SW.Stop();
Console.WriteLine("遍歷【學生類】字典(有鎖) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
Console.WriteLine("--------------------------------------------------------");
//遍歷HashTable(無鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CurrentItem = _Ht[i].ToString();
}
_SW.Stop();
Console.WriteLine("遍歷【HashTable】字典(無鎖) 花費時間爲:{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】字典(有鎖) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//遍歷HashTable類(無鎖)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CurrentStudent = (Student)_HtClass[i];
}
_SW.Stop();
Console.WriteLine("遍歷【HashTable學生類】字典(無鎖) 花費時間爲:{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學生類】字典(有鎖) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
Console.WriteLine("--------------------------------------------------------");
//遍歷ConCurrent字典
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CurrentItem = _CctDic[i];
}
_SW.Stop();
Console.WriteLine("遍歷【ConCurrent字典】(字符串) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
//遍歷ConCurrent字典(類)
_SW.Restart();
for (int i = 0; i < _NUM; i++)
{
_CurrentStudent = _CctDicClass[i];
}
_SW.Stop();
Console.WriteLine("遍歷【ConCurrent字典】(學生類) 花費時間爲:{0} 毫秒", _SW.Elapsed.TotalMilliseconds);
Console.WriteLine("--------------------------------------------------------");
_SW.Restart();
Console.WriteLine("-------------------結束---------------------------");
Console.ReadLine();
}
}//Class_end
public class Student
{
public string Name;
public int Age;
}
}