2011-08-14 49 views
4

在延续了我对C#和.NET锁最新的几点思考,C#锁定机制 - 只写锁定

考虑以下情形:

我有一个包含一个特定的集合(本例中的一类,我用了一个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是专门锁定关键部分,其刷新的对象dicti onary的内容。

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的类中添加一个布尔成员以更新或不更新字典并在“只读”方法中对其进行检查,但这会产生其他基于种族条件的问题...

任何想法如何优雅地解决这个问题?再次

感谢,

米奇

回答

8

看看内置的ReaderWriterLock

+0

太棒了,正是我所需要的... –

+2

@Mikey请务必阅读有关它的问题和ReaderWriterLockSlim –

+0

您可以发表一些相关的链接吗? –

4

我只是切换到使用一个ConcurrentDictionary完全避免这种情况 - 手动锁定容易出错。同样,我可以从"C#: The Curious ConcurrentDictionary"收集,ConcurrentDictionary已被读取优化。

+0

的并发集合是非常好的,如果你是在.NET 4 –

+0

我在.NET 4中,但我实际上更感兴趣的是解决方案的理论思想以及适合旧版.NET的解决方案,谢谢! –

0

在C#4.0中有更快的ReaderWriterLockSlim类! 几乎和锁一样快()。

保持策略不允许递归(LockRecursionPolicy :: NoRecursion)以保持性能如此之高。

Look at this page for more info.

+0

'ReaderWriterLockSlim'自3.5以来一直在框架中... –

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; 
}