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();
}
运行结果
运行会得到一组比较诡异的结果:
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
使用 lock
关键字时的写入操作,多线程的耗时明显比单线程多很多(55ms => 333ms),因为单线程只迭代了 100w 次,而多线程时开启了 10 个线程,总共迭代了 1000w 次,耗时增加很容易理解;
使用 ConcurrentDictionary
类时的写入操作,多线程的耗时反而比单线程时少了(350ms => 180ms),即便是迭代次数是 10 倍的情况下。
读取操作不管是单线程还是多线程,使用 ConcurrentDictionary
类的效率均是稍微高一些。
总结
如果是读取多而写入少的情况,始终推荐使用 ConcurrentDictionary
类;
写入多时,如果是多线程则推荐使用 ConcurrentDictionary
类;单线程则直接使用 Dictionary
类就可以了(不需要使用 lock
关键字,上面的例子仅仅是为了演示)。