所以我在ConcurrentDictionary中有一个IList作为值.
ConcurrentDictionary<int, IList<string>> list1 = new ConcurrentDictionary<int, IList<string>>;
Run Code Online (Sandbox Code Playgroud)
为了更新列表中的值,我这样做:
if (list1.ContainsKey[key])
{
IList<string> templist;
list1.TryGetValue(key, out templist);
templist.Add("helloworld");
}
Run Code Online (Sandbox Code Playgroud)
但是,在templist中添加字符串是否会更新ConcurrentDictionary?如果是,那么更新是否是线程安全的,以便不会发生数据损坏?
或者是否有更好的方法来更新或创建ConcurrentDictionary中的列表
编辑
如果我使用ConcurrentBag而不是List,我将如何实现它?更具体地说,我该如何更新它?ConcurrentDictionary的TryUpdate方法感觉有点过分.
ConcurrentBag.Add是否在线程安全的mannar中更新ConcurrentDictionary?
ConcurrentDictionary<int, ConcurrentBag<string>> list1 = new ConcurrentDictionary<int, ConcurrentBag<string>>
Run Code Online (Sandbox Code Playgroud)
可以说,线程安全字典上的操作按键来说是线程安全的。因此,只要您IList<T>仅从一个线程访问您的值(在本例中为 ),就可以开始了。
ConcurrentDictionary 不会阻止两个线程同时访问属于一个键的值。
首先,不需要执行ContainsKey() 和 TryGetValue()。
您应该这样做:
IList<string> templist;
if (list1.TryGetValue(key, out templist))
templist.Add("helloworld");
Run Code Online (Sandbox Code Playgroud)
实际上,您编写的代码具有竞争条件。
在一个线程调用ContainsKey()与TryGetValue()另一个线程之间可能已使用该键删除了该项目。然后TryGetValue()将返回tempListnull,然后在调用时会得到null引用异常tempList.Add()。
其次,是的:这里还有另一个可能的线程问题。您不知道IList<string>字典中存储的内容是线程安全的。
因此tempList.Add(),不能保证通话安全。
您可以使用ConcurrentQueue<string>代替IList<string>。这可能将是最可靠的解决方案。
请注意,仅锁定访问权限IList<string>是不够的。
这不好:
if (list1.TryGetValue(key, out templist))
{
lock (locker)
{
templist.Add("helloworld");
}
}
Run Code Online (Sandbox Code Playgroud)
除非您在IList可能会访问的其他位置也使用相同的锁。这不容易实现,因此最好使用a ConcurrentQueue<>或向此类添加锁定并更改体系结构,以使其他任何线程都不能访问基础IList。
您可以使用 ConcurrentDictionary.AddOrUpdate 方法以线程安全的方式将项目添加到列表中。它更简单并且应该可以正常工作。
var list1 = new ConcurrentDictionary<int, IList<string>>();
list1.AddOrUpdate(key,
new List<string>() { "test" }, (k, l) => { l.Add("test"); return l;});
Run Code Online (Sandbox Code Playgroud)
UPD
根据文档和消息来源,传递给AddOrUpdate方法的工厂将超出锁定范围,因此List在工厂委托内调用方法不是线程安全的。
请参阅此答案下的评论。