【问题标题】:Using thread-safe ConcurrentDictionary collection使用线程安全的 ConcurrentDictionary 集合
【发布时间】:2014-10-21 14:07:56
【问题描述】:

我有一个这样的函数,它使用非线程安全的集合列表,也是线程安全的,它使用锁操作符:

public void ShowData(ref DataGridView dmRequests, ref DataGridView URL, ref DataGridView dmData, ref DataGridView errorCodes)
{
    List<KeyValuePair<string, ulong>> dmReqList = new List<KeyValuePair<string, ulong>>();
    List<KeyValuePair<string, ulong>> urlReqList = new List<KeyValuePair<string, ulong>>(); 
    List<KeyValuePair<string, ulong>> dmDataList = new List<KeyValuePair<string, ulong>>();
    List<KeyValuePair<string, ulong>> errCodesList = new List<KeyValuePair<string, ulong>>();

    lock (m_logStruct.domainName)
    {
        dmReqList = m_logStruct.domainName.ToList();
    }
    lock(m_logStruct.URL)
    {
        urlReqList = m_logStruct.URL.ToList();
    }
    lock(m_logStruct.domainData)
    {
        dmDataList = m_logStruct.domainData.ToList();
    }
    lock(m_logStruct.errorCodes)
    {
        errCodesList = m_logStruct.errorCodes.ToList();
    }

    dmRequests.DataSource = dmReqList.OrderBy(x => x.Key).ToList();
    URL.DataSource = urlReqList.OrderBy(x => x.Key).ToList();
    dmData.DataSource = dmDataList.OrderBy(x => x.Key).ToList();
    errorCodes.DataSource = errCodesList.OrderBy(x => x.Key).ToList();
}

我想将其转换为无锁。为此,我在这个函数中使用了 ConcurrentDictionary 集合而不是 List 集合,所以我的函数开始看起来是这样的:

public void ShowData(ref DataGridView dmRequests, ref DataGridView URL, ref DataGridView dmData, ref DataGridView errorCodes)
{
    try
    {
        ConcurrentDictionary<string, ulong> dmReqList = new ConcurrentDictionary<string, ulong>();
        ConcurrentDictionary<string, ulong> urlReqList = new ConcurrentDictionary<string, ulong>();
        ConcurrentDictionary<string, ulong> dmDataList = new ConcurrentDictionary<string, ulong>();
        ConcurrentDictionary<string, ulong> errCodesList = new ConcurrentDictionary<string, ulong>();

        dmReqList = m_logStruct.domainName;
        urlReqList = m_logStruct.URL;
        dmDataList = m_logStruct.domainData;
        errCodesList = m_logStruct.errorCodes;

        dmRequests.DataSource = dmReqList.OrderBy(x => x.Key);
        URL.DataSource = urlReqList.OrderBy(x => x.Key).ToList();//I get error here: Index is out of range                
        dmData.DataSource = dmDataList.OrderBy(x => x.Key).ToList();              
        errorCodes.DataSource = errCodesList.OrderBy(x => x.Key).ToList();
    }
    catch(IOException e)
    {
        MessageBox.Show(e + " Something bad has been occurred here!");
    }

}

但是在这个函数中我开始得到错误(索引超出范围)。如何正确使用线程安全集合(ConcurrentDictionary)?如何解决我的错误?

【问题讨论】:

  • 您声明了新词典,但随后立即将其覆盖。你可以在函数中去掉你所有的new ConcurrentDictionary&lt;string, ulong&gt;();,它们不会为你做任何事情。
  • 您的示例显示了无法将变量公开给多个线程(所有局部变量)的代码 - 请考虑更新您的代码以实际显示诸如 errCodesList 之类的内容如何被多个线程访问。
  • 旁注:请考虑将样本缩减为仅一组集合 - 每个函数中有 4 个几乎相同的部分不会提供任何额外信息。

标签: c# multithreading thread-safety concurrentdictionary


【解决方案1】:

如果您要替换存储列表的变量,它不会是并发的。您需要使用它提供的并发方法。请参阅MSDN article 中的示例。

使用TryAddTryUpdateTryRemoveAddOrUpdate 来添加到列表或更新列表,而不是:

dmReqList = m_logStruct.domainName;

执行以下操作:

foreach (var item in m_logStruct.domainName)
{
    dmReqList.TryAdd(item.Key, item.Value);
}

更新: 正如 Alexei 所指出的,您正在本地定义 dmReqList。这意味着您不会从它作为 ConcurrentDictionary 中获得任何好处,因为无论如何其他线程都无法访问它。此外,您从原始示例中暗示 m_logStruct 可以通过其他线程访问,因为您对其应用了锁。如果暗示是正确的,那么这就是需要成为 ConcurrentDictionary 的内容。因此,您的代码需要看起来更像这样:

private ConcurrentDictionary<string, ulong> _domainNames = new ConcurrentDictionary<string, ulong>();

public void ShowData(ref DataGridView dmRequests)
{
    dmRequests.DataSource = _domainNames.OrderBy(x => x.Key);  //Set the current values of the dictionary to the grid.
}

public void MultiThreadedMethod()
{
    _domainNames.TryAdd(someKey, someValue);  //Access the dictionary from multiple threads.
}

【讨论】:

  • 请注意,虽然在某种程度上回答了有关如何使用ConcurrentDictionary 的问题,但此建议允许从多个线程访问m_logStruct,因此完全不是线程安全的。与常规列表/字典相比,将ConcurrentDictionary 用于任何其他线程不可见的局部变量不会使代码更加“线程安全”。
  • OP 没有提供 m_logStruct 的定义,但是根据约定,我们可以假设这是一个私有的全局变量。诚然,如果 m_logStruct 可以被多个更改它的线程访问,它“可能”不是完全线程安全的,但我们只需要假设 OP 已经知道这一点,因为他不担心访问 m_logStruct,而是担心 dmReqList。此外,dmReqList 不是局部变量。如果您查看 OP 的方法定义,他会通过引用将其传递给方法。
  • "dmReqList 不是局部变量" - 它是什么?请注意,原始代码锁定在 lock (m_logStruct.domainName) 上,所以看起来像 m_logStruct 并且它的字段需要保护......(注意提供的示例有点随机代码 - 即通过 ref 传递所有参数或锁定字段而不是单独的对象 - 所以它可能意味着 OP 不太关心该特定代码,只是对使用 ConcurrentDictionary 的提示感兴趣)
  • 哦,你说得对 dmReqList。我读错了。我将更新我的答案,这也将解决您的其他陈述。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多