When working on a project recently, Dictionary
when using global variables in multi-threading, I found that the data was not stored in it Dictionary
, but the program did not report an error. After some investigation, I found that Dictionary is a non-thread-safe type, so I feel that the data is not The reason for writing it in is that when multiple threads compete for global variables, the data is not written, so I went to have a Dictionary
careful understanding of it.
After checking the information on the Internet, I found that the most explained are Dictionary
(non-thread-safe) and ConcurrentDictionary
(thread-safe), so I also explained these two keywords carefully, and by the way, I also got a deeper understanding of them.
Dictionary
How to solve the thread safety problem? You can use locks, thread global variables, use, ConcurrentDictionary
etc. Let's take a look together, Let's go.
Dictionary
Dictionary<TKey, TValue>
The generic class provides a key-value pair mapping. TKey
The speed of retrieving values through it is very fast, and the time complexity is close to O(1). This is because it is Dictionary
implemented through a hash table, which is a disguised form HashTable
and adopts the data of the separated link hash table structure to solve the hash collision problem.
In earlier versions of C#, you could use a collection initializer for sequence-style collections, obtained by adding parentheses around key-value pairs, Dictionary<TKey, TValue>
as in:
Dictionary<int, string> msgs = new Dictionary<int, string>()
{
{
1, "Hello, "},
{
2 , "World"},
{
3, "!"}
};
The new syntax supports assigning to collections using indexes, such as:
Dictionary<int, string> MsgErrs = new Dictionary<int, string>()
{
[1] = "Hello, ",
[2] = "World",
[3] = "!",
};
key
The above two are similar in initialization assignment, but there are still some differences between the two. The former will report an error directly if there are duplicate values during initialization . When the latter is initialized, key
there can be duplicate values, the system will automatically filter out duplicate key
values, and the program will not report an error.
Implementing a collection of key/value pairs.
Each addition to the dictionary contains a value with its associated value, which is conveniently retrieved by using the key;
if you use the collection initializer to generate Dictionary
the collection, you can use the following method:
public static Dictionary<string, Element> BuildDic()
{
return new Dictionary<string, Element>
{
{
"L", new Element(){
Symbol = "L", Name = "Postass", AutominNumber = 9}},
{
"Q", new Element(){
Symbol = "Q", Name = "Calcium", AutominNumber = 99}},
{
"M", new Element(){
Symbol = "JY", Name = "JYaoiang", AutominNumber=7924}}
};
}
Dictionary add value
public static void IterateDictionary()
{
Dictionary<string, Element> element = BuildDic();
foreach(KeyValuePair<string, Element> keyValue in element)
{
Element ele = keyValue.Value;
Console.WriteLine(string.Format("Key={0}; Values={0};{1};{2}", keyValue.Key, ele.Symbol, ele.Name, ele.AutominNumber));
}
}
public static Dictionary<string, Element> BuildDictionary()
{
var elements = new Dictionary<string, Element>();
AddDictionary(elements, "L", "LLL", 9);
AddDictionary(elements, "J", "LJLHHH", 19);
AddDictionary(elements, "A", "ABABABA", 20);
return elements;
}
public static void AddDictionary(Dictionary<string, Element> elements, string symbol, string name, int num)
{
Element ele = new Element()
{
Symbol = symbol,
Name = name,
AutominNumber = num
};
elements.Add(key: symbol, value: ele);
}
ContainsKey method and Item[] property
public static void FindDictionary(string symbol)
{
Dictionary<string, Element> elements = BuildDictionary();
if (elements.ContainsKey(symbol))
{
Element ele = elements[symbol];
Console.WriteLine("Found: " + ele.Name);
}
else
{
Console.WriteLine("Not found " + symbol);
}
}
TryGetValue method
public static void FindDictionaryOfTryGetValue(string symbol)
{
Dictionary<string, Element> elements = BuildDictionary();
Element ele = null;
if(elements.TryGetValue(symbol, out ele))
{
Console.WriteLine("Found: " + ele.Name);
}
else
{
Console.WriteLine("Not found " + symbol);
}
}
Here we explain the common usage methods of Dictionary. Here is a long-winded sentence. I don’t know if you pay attention to the Add method and TryAdd method when using Dictionary. What is the difference between these two methods?
We all know that when adding a key value to the Dictionary, the key cannot be repeated. If you use the Add method to add a duplicate key, the program will report an error. To avoid this problem, you can use the TryAdd method. When adding duplicate key values, this method will return false to avoid such problems.
ConcurrentDictionary
In .NET Framework 4
and later, System.Collections.Concurrent
collections in namespaces provide efficient thread-safe operations for accessing collection items from multiple threads.
When multiple threads access collection items, you should use System.Collections.Concurrent
the classes in the namespace instead of using System.Collections.Generic
the classes in the System.Collections namespace.
System.Collections.Concurrent
Classes in the namespace: BlockingCollection, ConcurrentDictionary<TKey, TValue>、ConcurrentQueue<T>、ConcurrentStack<T>
.
System.Collections
Classes in a namespace do not store elements as specially typed objects, but as object
objects of type.
ConcurrentDictionary
The usage is Dictionary
similar to and will not be explained in detail here. But ConcurrentDictionary
only TryAdd
method Dictionary
can be used instead of Add
and TryAdd
method.
Dictionary and ConcurrentDictionary multithreading
After taking everyone to know Dictionary
and ConcurrentDictionary
, let's return to the topic and see how the two are used in multi-threading.
code show as below:
ConcurrentDictionary<int, string> keys = new ConcurrentDictionary<int, string>();
keys.TryAdd(1, "LL");
keys.TryAdd(2, "LL");
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "OJ");
dic.TryAdd(2, "R");
Stopwatch stopwatch = new Stopwatch();
#region 写入
stopwatch.Start();
Parallel.For(0, 10000000, i =>
{
lock (dic)
{
dic[i] = new Random().Next(100, 99999).ToString();
}
});
stopwatch.Stop();
Console.WriteLine("Dictionary加锁写入花费时间:{0}", stopwatch.Elapsed);
stopwatch.Restart();
Parallel.For(0, 10000000, i =>
{
keys[i] = new Random().Next(100, 99999).ToString();
});
stopwatch.Stop();
Console.WriteLine("ConcurrentDictionary加锁写入花费时间:{0}", stopwatch.Elapsed);
#endregion
#region 读取
string result = string.Empty;
stopwatch.Restart();
Parallel.For(0, 10000000, i =>
{
lock (dic)
{
result = dic[i];
}
});
stopwatch.Stop();
Console.WriteLine("Dictionary加锁读取花费时间:{0}", stopwatch.Elapsed);
stopwatch.Restart();
Parallel.For(0, 10000000, i =>
{
result = keys[i];
});
stopwatch.Stop();
Console.WriteLine("ConcurrentDictionary加锁读取花费时间:{0}", stopwatch.Elapsed);
#endregion
Console.ReadLine();
It can be found that under multi-threading, the added lock
write Dictionary
performance is ConconcurrentDictionary
better than the written performance, and the performance of reading data ConcurrentDictionary is better.
When we increase the written data to 20000000, ConcurrentDictionary
the write performance is obviously Dictionary
worse than the performance, but the read performance ConcurrentDictionary
is better.
When we increase the written data to 2000000, ConcurrentDictionary
the write performance is still Dictionary
worse than the performance, but the read performance ConcurrentDictionary
is better.
In summary, after comparing the two, ConcurrentDictionary
the read performance is better and Dictionary
the write performance is better.
As for the specific reason, I will explain it in depth at that time. This article roughly ends here, and we will see you in the next article.