继续讨论关于C#和.NET中的锁的最新思考,
考虑以下情况:
我有一个类,其中包含一个特定的集合(对于此示例,我使用了Dictionary<string, int>
),它使用特定的方法每隔几分钟从数据源更新一次,您可以在下面看到它:
DataTable dataTable = dbClient.ExecuteDataSet(i_Query).GetFirstTable();
lock (r_MappingLock)
{
i_MapObj.Clear();
foreach (DataRow currRow in dataTable.Rows)
{
i_MapObj.Add(Convert.ToString(currRow[i_Column1]), Convert.ToInt32[i_Column2]));
}
}
r_MappingLock是一个专门用于锁定关键部分的对象,用于刷新字典的内容。
i_MapObj是字典对象
i_Column1和i_Column2是数据表的列名,其中包含映射所需的数据。
现在,我还有一个类方法,它接收一个字符串并根据提到的字典返回正确的映射int。
我希望这个方法等到刷新方法完成它的执行,所以乍看之下会考虑以下实现:
lock (r_MappingLock)
{
int? retVal = null;
if (i_MapObj.ContainsKey(i_Key))
{
retVal = i_MapObj[i_Key];
}
return retVal;
}
这将防止在更新字典时出现意外行为和返回值,但会出现另一个问题: 由于执行上述方法的每个线程都试图声明锁定,这意味着如果多个线程同时尝试执行此方法,则每个线程都必须等到上一个线程完成执行该方法并尝试声明锁定,这显然是一种不良行为,因为上述方法仅用于读取目的。
我正在考虑在类中添加一个布尔成员,该类将被设置为true或false,字典是否正在更新,并在“只读”方法中检查它,但这会产生其他基于竞争条件的问题...
任何想法如何优雅地解决这个问题?
再次感谢,
米奇
答案 0 :(得分:8)
查看内置的ReaderWriterLock
。
答案 1 :(得分:4)
我只想切换到使用ConcurrentDictionary
来完全避免这种情况 - 手动锁定容易出错。另外,我可以从"C#: The Curious ConcurrentDictionary"收集,ConcurrentDictionary
已经过读取优化。
答案 2 :(得分:2)
Albin在ReaderWriterLock正确指出。我将添加一个更好的一个:杰弗里里希特ReaderWriterGate。享受!
答案 3 :(得分:1)
您可以考虑在更新时创建新字典,而不是锁定。这样,您将始终获得一致的结果,但更新期间的读取将返回以前的数据:
private volatile Dictionary<string, int> i_MapObj = new Dictionary<string, int>();
private void Update()
{
DataTable dataTable = dbClient.ExecuteDataSet(i_Query).GetFirstTable();
var newData = new Dictionary<string, int>();
foreach (DataRow currRow in dataTable.Rows)
{
newData.Add(Convert.ToString(currRow[i_Column1]), Convert.ToInt32[i_Column2]));
}
// Start using new data - reference assignments are atomic
i_MapObj = newData;
}
private int? GetValue(string key)
{
int value;
if (i_MapObj.TryGetValue(key, out value))
return value;
return null;
}
答案 4 :(得分:0)
在C#4.0中,ReaderWriterLockSlim类速度更快! 几乎和lock()一样快。
保持策略禁止递归(LockRecursionPolicy :: NoRecursion)以保持高性能。