服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#并发容器之ConcurrentDictionary与普通Dictionary带锁性能详解

C#并发容器之ConcurrentDictionary与普通Dictionary带锁性能详解

2022-11-10 14:38conquerwave C#

这篇文章主要介绍了C#并发容器之ConcurrentDictionary与普通Dictionary带锁性能详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

结果已经写在注释中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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
    //对于写入操作并发词典要比普通带锁词典要慢
    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
    //对于读取操作并发词典要比普通带锁词典要快
    //concurrentDictionary采用细粒度锁定[fine-grained locking]
    //普通带锁dictionary采用粗粒度锁定[coarse-grained locking]
    //在多核多线程的情况下concurrentDictionary将有更好的性能表现
    sw.Restart();
    Console.ReadKey();
}
const string Item = "Dictionary item";
public static string CurrentItem;

补充:C#中普通字典(Dictionary)、并发字典(ConcurrentDictionary)、和哈希表(Hashtable)读写性能比较

一、说明

程序有时候需要并发多线程操作,多线程读取同一个容器内的东西是可以的,但是如果需要修改及写入到同一容器内,会有索引失败的问题,即两个进程同时向同一个位置写入内容,这种情况下需要通过lock(var),将容器锁定,也可以直接使用可并发读写的容器(ConcurrentDictionary)

测试分2部分,一次是写入操作,包含带锁写入和不带锁写入,其中每个里面又细分为写入字符串和写入一个类,还有一次是遍历操作,同样包含带锁读和不带锁读,其中也分为读取字符串和读取类。

二、测试结果

C#并发容器之ConcurrentDictionary与普通Dictionary带锁性能详解

2.1、写入用时

C#并发容器之ConcurrentDictionary与普通Dictionary带锁性能详解

2.2、遍历用时

C#并发容器之ConcurrentDictionary与普通Dictionary带锁性能详解

2.3、结论

对于写入操作速度:普通词典 > HashTable > 并发词典

对于读操作速度:并发字典 > 带锁字典 > HashTable

无论普通字典还是HashTable,带锁花费的时间都要比不带锁慢,为了线程安全,肯定要牺牲时间的。

所以如果需要自己写入的话,推荐带锁普通字典,读写速度都很均衡。

三、测试代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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;
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://blog.csdn.net/conquerwave/article/details/50815557

延伸 · 阅读

精彩推荐
  • C#C#开发微信门户及应用(5) 用户分组信息管理

    C#开发微信门户及应用(5) 用户分组信息管理

    这篇文章主要为大家详细介绍了C#开发微信门户及应用第五篇,用户分组信息管理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    伍华聪9882022-01-10
  • C#C#创建WCF服务控制台应用程序详解

    C#创建WCF服务控制台应用程序详解

    这篇文章主要为大家详细介绍了C#创建WCF服务控制台应用程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    cnc6912022-01-17
  • C#C#中委托的基本用法总结

    C#中委托的基本用法总结

    以下是对C#中委托的基本用法进行了详细的总结分析,需要的朋友可以过来参考下。希望对大家有所帮助...

    C#教程网10312021-01-04
  • C#C#编写一个简单记事本功能

    C#编写一个简单记事本功能

    这篇文章主要为大家详细介绍了C#编写一个简单记事本功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    卖画的作家9992022-01-25
  • C#C#创建压缩文件的实现代码

    C#创建压缩文件的实现代码

    本篇文章主要介绍了C# 创建压缩文件的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    sparkdev11942022-01-04
  • C#C#清除WebBrowser中Cookie缓存的方法

    C#清除WebBrowser中Cookie缓存的方法

    这篇文章主要介绍了C#清除WebBrowser中Cookie缓存的方法,涉及C#针对WebBrowser控件的操作技巧,非常简单实用,需要的朋友可以参考下...

    Demon6112021-11-22
  • C#解决C#调用dll提示"试图加载格式不正确的程序"问题

    解决C#调用dll提示"试图加载格式不正确的程序"问题

    下面小编就为大家分享一篇解决C#调用dll提示"试图加载格式不正确的程序"问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    柄棋先生11962022-02-17
  • C#C# 获取动态key的json对象的值案例

    C# 获取动态key的json对象的值案例

    这篇文章主要介绍了C# 获取动态key的json对象的值案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    冬冬冬冬冬冬冬冬冬冬4732022-10-31