C# 多线程 06-使用并发集合 01-使用 ConcurrentDictionary
🏷️ 《C# 多线程》
使用 ConcurrentDictionary
csharp
const string Item = "Dictionary item";
const int Iterations = 1000000;
public static string CurrentItem;
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 < Iterations; i++)
{
lock (dictionary)
{
dictionary[i] = Item;
}
}
sw.Stop();
Console.WriteLine($"写入使用粗粒度锁的字典: {sw.Elapsed}");
sw.Restart();
for (int i = 0; i < Iterations; i++)
{
concurrentDictionary[i] = Item;
}
sw.Stop();
Console.WriteLine($"写入一个 ConcurrentDictionary(细粒度锁): {sw.Elapsed}");
sw.Restart();
for (int i = 0; i < Iterations; i++)
{
lock (dictionary)
{
CurrentItem = dictionary[i];
}
}
sw.Stop();
Console.WriteLine($"使用粗粒度锁从字典中读取: {sw.Elapsed}");
sw.Restart();
for (int i = 0; i < Iterations; i++)
{
CurrentItem = concurrentDictionary[i];
}
sw.Stop();
Console.WriteLine($"从一个 ConcurrentDictionary 中读取: {sw.Elapsed}");
Console.WriteLine();
int taskSize = 10;
Task[] ts = new Task[taskSize];
for (int j = 0; j < taskSize; j++)
{
Task t = new Task(() => {
for (int i = 0; i < Iterations; i++)
{
lock (dictionary)
{
dictionary[i] = Item;
}
}
});
ts[j] = t;
}
var whenAllTask = Task.WhenAll(ts);
sw.Restart();
for (int i = 0; i < ts.Length; i++)
{
ts[i].Start();
}
whenAllTask.Wait();
sw.Stop();
Console.WriteLine($"【多线程】写入使用粗粒度锁的字典: {sw.Elapsed}");
ts = new Task[taskSize];
for (int j = 0; j < taskSize; j++)
{
Task t = new Task(() => {
for (int i = 0; i < Iterations; i++)
{
concurrentDictionary[i] = Item;
}
});
ts[j] = t;
}
whenAllTask = Task.WhenAll(ts);
sw.Restart();
for (int i = 0; i < ts.Length; i++)
{
ts[i].Start();
}
whenAllTask.Wait();
sw.Stop();
Console.WriteLine($"【多线程】写入一个 ConcurrentDictionary(细粒度锁): {sw.Elapsed}");
ts = new Task[taskSize];
for (int j = 0; j < taskSize; j++)
{
Task t = new Task(() => {
for (int i = 0; i < Iterations; i++)
{
lock (dictionary)
{
CurrentItem = dictionary[i];
}
}
});
ts[j] = t;
}
whenAllTask = Task.WhenAll(ts);
sw.Restart();
for (int i = 0; i < ts.Length; i++)
{
ts[i].Start();
}
whenAllTask.Wait();
sw.Stop();
Console.WriteLine($"【多线程】使用粗粒度锁从字典中读取: {sw.Elapsed}");
ts = new Task[taskSize];
for (int j = 0; j < taskSize; j++)
{
Task t = new Task(() => {
for (int i = 0; i < Iterations; i++)
{
CurrentItem = concurrentDictionary[i];
}
});
ts[j] = t;
}
whenAllTask = Task.WhenAll(ts);
sw.Restart();
for (int i = 0; i < ts.Length; i++)
{
ts[i].Start();
}
whenAllTask.Wait();
sw.Stop();
Console.WriteLine($"【多线程】从一个 ConcurrentDictionary 中读取: {sw.Elapsed}");
Console.ReadLine();
}
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
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
运行结果
运行会得到一组比较诡异的结果:
txt
写入使用粗粒度锁的字典: 00:00:00.0558413
写入一个ConcurrentDictionary(细粒度锁): 00:00:00.3505142
使用粗粒度锁从字典中读取: 00:00:00.0281449
从一个ConcurrentDictionary中读取: 00:00:00.0227328
【多线程】写入使用粗粒度锁的字典: 00:00:00.3331349
【多线程】写入一个ConcurrentDictionary(细粒度锁): 00:00:00.1801702
【多线程】使用粗粒度锁从字典中读取: 00:00:00.3150063
【多线程】从一个ConcurrentDictionary中读取: 00:00:00.1925646
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
使用 lock
关键字时的写入操作,多线程的耗时明显比单线程多很多(55ms => 333ms),因为单线程只迭代了 100w 次,而多线程时开启了 10 个线程,总共迭代了 1000w 次,耗时增加很容易理解;
使用 ConcurrentDictionary
类时的写入操作,多线程的耗时反而比单线程时少了(350ms => 180ms),即便是迭代次数是 10 倍的情况下。
读取操作不管是单线程还是多线程,使用 ConcurrentDictionary
类的效率均是稍微高一些。
总结
如果是读取多而写入少的情况,始终推荐使用 ConcurrentDictionary
类;
写入多时,如果是多线程则推荐使用 ConcurrentDictionary
类;单线程则直接使用 Dictionary
类就可以了(不需要使用 lock
关键字,上面的例子仅仅是为了演示)。