这个类线程安全吗?

时间:2009-04-27 16:39:35

标签: c# multithreading

这个ValueStore类线程是否安全? GetInt(字符串键)中的锁定范围是否需要在yield return周围扩展?

public class ValueStore
{
  private readonly object _locker = new object();
  private readonly Dictionary<string, int> _data = 
    new Dictionary<string, int>();

  public ValueStore(Dictionary<string, int> data)
  {
    _data = data;
  }

  public IEnumerable<int> GetInt(string key)
  {
    IEnumerable<KeyValuePair<string, int>> selected;
    lock(_locker)
    {
      selected = _data.Where(x => x.Key.Equals(key));
    }

    foreach (KeyValuePair<string, int> pair in selected)
    {
      yield return pair.Value;
    }
  }
}

单元测试似乎没问题:

[TestFixture]
public class ValueStoreTest
{
  [Test]
  public void test1()
  {
    Dictionary<string, int> data = new Dictionary<string, int>();
    for (int i = 0; i < 100000; i++)
    {
      data.Add(i.ToString(),i);
    }

    ValueStore vs = new ValueStore(data);

    for (int i = 0; i < 900000; i++)
    {
      ThreadPool.QueueUserWorkItem(delegate
      {
        for (int j = 0; j < 100000; j++)
        {
          IEnumerable<int> d = vs.GetInt(j.ToString());
        }
      });
    }
  }
}

3 个答案:

答案 0 :(得分:6)

不,它绝对不是线程安全的。

它使用客户端传入的字典这一事实意味着您无法控制客户端何时更改它。您也只是在应用Where子句时锁定它 - 但实际上根本不执行任何迭代。在迭代结果时你需要保持锁定 - 但正如我之前所说,它不会阻止客户端随时更改字典。

如果您在类中创建了字典并且只在其中公开了数据(即保护它不受外界影响),则可以使其完全是线程安全的。如果你坚持认为客户端代码不会改变字典,你就根本不需要锁,因为当没有编写器时,字典可以安全地从多个线程中读取。

答案 1 :(得分:1)

我可以告诉你,在释放锁之后才会执行锁中的语句。如果必须在迭代期间锁定集合,则将yield移动到lock语句中。

答案 2 :(得分:1)

不,不是。如果您开始阅读并写入传递给构造函数的Dictionary<string, int>对象,则会出现问题。您的_data类声明会立即被构造函数中的赋值覆盖。

要更正此问题,请将构造函数中传入的Dictionary中的每个键/值对复制到您的班级Dictionary中,而不是直接分配。

然后,我认为你是线程安全的,但显然你的课程是只读的。