gpt4 book ai didi

c# - 并发字典的线程安全重新初始化

转载 作者:行者123 更新时间:2023-11-30 16:05:50 25 4
gpt4 key购买 nike

我想知道以下代码是否是线程安全的,我认为它不是。我怎样才能让它成为线程安全的?

基本上,我有一个 ConcurrentDictionary 充当数据库表的缓存。我想每 10 秒查询一次数据库并更新数据库缓存。整个过程中都会有其他线程查询这本字典。

我不能只使用 TryAdd,因为可能还有已被删除的元素。所以我决定不搜索整个字典来更新、添加或删除。我只想重新初始化字典。如果这是一个愚蠢的想法,请告诉我。

我担心的是,当我重新初始化字典时,查询线程在初始化发生时将不再是实例的线程安全。出于这个原因,我在更新字典时使用了一个锁,但是我不确定这是否正确,因为锁中的对象发生了变化?

private static System.Timers.Timer updateTimer;
private static volatile Boolean _isBusyUpdating = false;
private static ConcurrentDictionary<int, string> _contactIdNames;

public Constructor()
{
// Setup Timers for data updater
updateTimer = new System.Timers.Timer();
updateTimer.Interval = new TimeSpan(0, 0, 10, 0).TotalMilliseconds;
updateTimer.Elapsed += OnTimedEvent;
// Start the timer
updateTimer.Enabled = true;
}

private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
{
if (!_isBusyUpdating)
{
_isBusyUpdating = true;
// Get new data values and update the list
try
{
var tmp = new ConcurrentDictionary<int, string>();
using (var db = new DBEntities())
{
foreach (var item in db.ContactIDs.Select(x => new { x.Qualifier, x.AlarmCode, x.Description }).AsEnumerable())
{
int key = (item.Qualifier * 1000) + item.AlarmCode;
tmp.TryAdd(key, item.Description);
}
}
if (_contactIdNames == null)
{
_contactIdNames = tmp;
}
else
{
lock (_contactIdNames)
{
_contactIdNames = tmp;
}
}
}
catch (Exception e)
{
Debug.WriteLine("Error occurred in update ContactId db store", e);
}
_isBusyUpdating = false;
}
}

/// Use the dictionary from another Thread
public int GetIdFromClientString(string Name)
{
try
{
int pk;
if (_contactIdNames.TryGetValue(Name, out pk))
{
return pk;
}
}
catch { }
//If all else fails return -1
return -1;
}

最佳答案

你是对的,你的代码不是线程安全的。

  1. 您需要锁定 _isBusyUpdating 变量。
  2. 您需要每次都锁定 _contactIdNames,而不仅仅是当它不是 null 时。

此代码也类似于 singleton pattern它与初始化有同样的问题。你可以用 Double checked locking 来解决.但是,在访问条目时,您还需要双重检查锁定。

在一次更新整个字典的情况下,每次访问时都需要锁定当前值。否则你可以在它还在变化的时候访问它并得到错误。所以你要么每次都需要锁定变量,要么使用 Interlocked .

作为MSDN says volatile 应该用 _isBusyUpdating 来解决这个问题,它应该是线程安全的。

如果您不想跟踪 _contactIdNames 线程安全,请尝试对同一字典中的每个条目进行更新。问题在于 DB 和当前值之间的差异检测(删除或添加了哪些条目,其他条目可以简单地重写),而不是线程安全,因为 ConcurrentDictionary 已经是线程安全的。

关于c# - 并发字典的线程安全重新初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33076762/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com