測試條件:數組
開啓2個並行執行任務,往同一個list對象寫入值安全
測試代碼:多線程
static int maxNum = 1000000; static List<int> list = new List<int>(); static void Main(string[] args) { //迭代次數 int iterationNum = 3; CodeTimer.Initialize(); CodeTimer.Time("List是不是線程安全的呢?", iterationNum, new Action(ListIsThreadSafe)); //Console.Write(sbIsThreadSafe.ToString()); Console.Read(); } private static void ListIsThreadSafe() { Parallel.For(1, maxNum / 2, (i) => { list.Add(i); }); Parallel.For(maxNum / 2 + 1, maxNum, (i) => { list.Add(i); }); }
測試結果:ide
測試結論:性能
之因此會形成以上的結果是由於list對象不是線程安全。那該怎麼辦呢? 測試
這時咱們須要使用System.Collections.Concurrent命名空間下的類型來用於並行循環體內。spa
類線程 |
說明3d |
BlockingCollection<T>code |
爲實現 IProducerConsumerCollection<T> 的線程安全 集合提供阻止和限制功能。 |
ConcurrentBag<T> |
表示對象的線程安全的無序集合。 |
ConcurrentDictionary<TKey, TValue> |
表示可由多個線程同時訪問的鍵值對的線程安全集合。 |
ConcurrentQueue<T> |
表示線程安全的先進先出 (FIFO) 集合。 |
ConcurrentStack<T> |
表示線程安全的後進先出 (LIFO) 集合。 |
OrderablePartitioner<TSource> |
表示將一個可排序數據源拆分紅多個分區的特定方式。 |
Partitioner |
提供針對數組、列表和可枚舉項的常見分區策略。 |
Partitioner<TSource> |
表示將一個數據源拆分紅多個分區的特定方式。 |
咱們再來進行一次測試。
測試代碼:
static int maxNum = 1000000; static ConcurrentQueue<int> safeList = new ConcurrentQueue<int>(); static void Main(string[] args) { //迭代次數 int iterationNum = 3; CodeTimer.Initialize(); CodeTimer.Time("ConcurrentQueue是不是線程安全的呢?", iterationNum, new Action(ListIsThreadSafe)); //Console.Write(sbIsThreadSafe.ToString()); Console.Read(); } private static void ListIsThreadSafe() { Parallel.For(1, maxNum / 2, (i) => { safeList.Enqueue(i); }); Parallel.For(maxNum / 2 + 1, maxNum, (i) => { safeList.Enqueue(i); }); }
測試結果:
測試結論:
ConcurrentQueue是線程安全的。在遇到多線程和並行開發時應該使用ConcurrentQueue而不是List.
疑問:爲了確保對象線程安全,系統底層實現機制一定是要使用對象加鎖和解鎖的功能來確保對象安全,那麼加鎖和解鎖是否會對性能形成影響呢?
咱們再來進行一組測試:
測試條件:
(1)對100萬個數據進行普通的循環運算而後添加到List集合對象中。
(2)對100萬個數據進行並行循環運算而後添加到ConcurrentQueue集合對象中。
測試代碼:
static int maxNum = 1000000; static List<BigInteger> list = new List<BigInteger>(); static ConcurrentQueue<BigInteger> safeList = new ConcurrentQueue<BigInteger>(); static void Main(string[] args) { //迭代次數 int iterationNum = 3; CodeTimer.Initialize(); CodeTimer.Time("普通的循環運算", iterationNum, new Action(NormalCompute)); CodeTimer.Time("並行循環運算_1", iterationNum, new Action(ParallelCompute_1)); CodeTimer.Time("並行循環運算_2", iterationNum, new Action(ParallelCompute_2)); Console.Read(); } private static void NormalCompute() { for (int i = 1; i <= maxNum; i++) { Math.Pow(i, i + 1); list.Add(new BigInteger(i)); } } private static void ParallelCompute_1() { Parallel.For(1, maxNum, (i) => { Math.Pow(i, i + 1); safeList.Enqueue(new BigInteger(i)); }); } private static void ParallelCompute_2() { Parallel.For(1, maxNum / 2, (i) => { Math.Pow(i, i + 1); safeList.Enqueue(new BigInteger(i)); }); Parallel.For(maxNum / 2 + 1, maxNum, (i) => { Math.Pow(i, i + 1); safeList.Enqueue(new BigInteger(i)); }); }
測試結果:
測試結論:
和我預期的結論是同樣的,並行方式所耗費的時間更加的長。線程安全的加鎖和解鎖對性能的影響仍是比較大的。