前言
我们在上位机软件开发过程中经常需要使用字典这个数据结构,并且经常会在多线程环境中使用字典,如果使用常规的Dictionary就会出现各种异常,本文就是详细介绍如何在多线程环境中使用字典来解决线程安全问题。
1、非线程安全举例
Dictionary<int, int> dic = new Dictionary<int, int>();private void TestAddNotSafe1(){while (true){for (int i = 0; i < 1000000; i++){if (dic.ContainsKey(i) == false){dic.Add(i, i);}Thread.Sleep(1);}break;}}private void TestAddNotSafe2(){while (true){for (int i = 0; i < 1000000; i++){if (dic.ContainsKey(i) == false){dic.Add(i, i);}Thread.Sleep(1);}break;}}
Task.Run(TestAddNotSafe1);Task.Run(TestAddNotSafe2);
上述代码运行后报错,如下:
这是当前线程判断没有该键,准备添加键值对的时候,另一个线程已经添加了该键,所以提示已经添加了相同的项。
2、线程安全举例
为了解决上面的问题,需要使用一个线程安全的字典,
ConcurrentDictionary<int, int> concurrentDictionary = new ConcurrentDictionary<int, int>();private void TestAddSafe1(){while (true){for (int i = 0; i < 1000000; i++){if (concurrentDictionary.ContainsKey(i) == false){if (concurrentDictionary.TryAdd(i, i)){}else{MessageBox.Show("错误1");}}Thread.Sleep(1);}break;}}private void TestAddSafe2(){while (true){for (int i = 0; i < 1000000; i++){if (concurrentDictionary.ContainsKey(i) == false){if (concurrentDictionary.TryAdd(i, i)){}else{MessageBox.Show("错误2");}}Thread.Sleep(1);}break;}}
Task.Run(TestAddSafe1);Task.Run(TestAddSafe2);
运行后弹出窗体,提示“错误1”或者“错误2”,但是软件没有崩溃,这是因为TryAdd方法调用返回了false,所以线程安全字典的好处是不会导致软件崩溃,添加值失败只会返回false而已。
总结:
对于字典的线程安全问题,除了TryAdd方法外还有TryRemove、TryUpdate方法。