Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET4.0:更新字典及其值的线程安全方式_C#_Multithreading_.net 4.0 - Fatal编程技术网

C# .NET4.0:更新字典及其值的线程安全方式

C# .NET4.0:更新字典及其值的线程安全方式,c#,multithreading,.net-4.0,C#,Multithreading,.net 4.0,我有一个静态字典,我想安全地更新。最初,字典将是空的,但在应用程序的生命周期内,它将添加新的值。此外,整数值将充当可以递增和递减的单个计数器 private static Dictionary<string, int> foo = new Dictionary<string, int>(); public static void Add(string bar) { if (!foo.ContainsKey(bar)) foo.Add(bar, 0

我有一个静态字典,我想安全地更新。最初,字典将是空的,但在应用程序的生命周期内,它将添加新的值。此外,整数值将充当可以递增和递减的单个计数器

private static Dictionary<string, int> foo = new Dictionary<string, int>();

public static void Add(string bar)
{
    if (!foo.ContainsKey(bar))
        foo.Add(bar, 0);

    foo[bar] = foo[bar] + 1;
}


public static void Remove(string bar)
{
    if (foo.ContainsKey(bar))
    {
        if (foo[bar] > 0)
            foo[bar] = foo[bar] - 1;
    }
}
然而,我的直觉是,当我在字典中添加新条目时,这并不能解决问题,所以我会立即想到锁:

private static Dictionary<string, int> foo = new Dictionary<string, int>();
private static object myLock = new object();

public static void Add(string bar)
{
    lock(myLock)
    {
        if (!foo.ContainsKey(bar))
            foo.Add(bar, 0);

        Interlocked.Increment(ref foo[bar]);
    }
}


public static void Remove(string bar)
{
    lock(myLock)
    {
        if (foo.ContainsKey(bar))
        {
            if (foo[bar] > 0)
                Interlocked.Decrement(ref foo[bar]);
        }
    }
}
private static Dictionary foo=new Dictionary();
私有静态对象myLock=新对象();
公共静态无效添加(字符串栏)
{
锁(myLock)
{
如果(!foo.ContainsKey(bar))
foo.Add(bar,0);
联锁增量(参考foo[bar]);
}
}
公共静态无效删除(字符串栏)
{
锁(myLock)
{
if(食品容器(棒材))
{
如果(foo[bar]>0)
联锁减量(参考foo[bar]);
}
}
}
这种方法是理想的,还是正确的?可以改进吗?我走远了吗?

是好的(而且是必要的;您正确地怀疑
联锁
不够),但一旦您这样做,
联锁。增量
联锁。减量
是不必要的。从多个线程访问
词典
的问题是,一个线程可能会触发内部哈希表的重建,然后该线程在重建过程中被替换为另一个线程,该线程现在出现并添加到词典中,对词典的内部结构造成严重破坏

此外,您的实现很好,因为您将
锁定到
私有对象上,而不是
这个
。正如所指出的,这个
lock
对象应该是
readonly

注意不要欺骗自己,让自己认为自己已经在多线程场景中实现了字典的防弹功能。例如,可能发生以下情况:

线程1在字典中查找字符串“Hello,World!”
,并接收回计数
1

线程1被替换为线程2

线程2使用键
调用remove“你好,世界!”
将计数设置为
0

线程2被替换为线程1

线程1现在认为计数是
1
,但实际上是
0


最后,在.NET 4中,您应该考虑使用。请注意,这有效地消除了在调用字典上的实例方法时对
锁定
的需要,但并不能避免出现上述情况。

如果使用.Net 4.0,可以考虑使用System.collections.Concurrent命名空间中的集合,例如,ConcurrentDictionary可能适合您。

在您的情况下,字典上的添加\删除操作必须是线程安全的。这是因为如果在一个线程上枚举字典,则不希望任何其他线程修改它

要创建线程安全字典,您应该在字典上创建一个包装器,它在私有IDictionary中内部维护数据,并使用诸如Add、Remove、Clear等锁定方法

有关如何做到这一点,请参阅本SO帖子-


或者,如果您使用的是.Net 4,则可以使用提供现成线程安全性的。

由于您在带有锁的Add and Remove方法中保护了对字典的多线程访问,因此不需要联锁语句,因为此代码位于这些锁的范围内。锁块中的所有代码现在都保证是单线程的,因此是安全的

这是一个很小的点,但您应该将myLock对象标记为只读,因为当前可以通过重新指定它来更改此对象。因此,您可以在一个线程锁定对象时更改该对象,随后的线程将看到不同的锁定对象,因此可以忽略以前的线程锁定。将对象标记为只读将使其不可变

Monitor.Enter()、Monitor.Exit()(lock(object))可能是您的最佳选择。但这段代码可以有所改进,尽管真正的好处可能微不足道

     private static Dictionary<string, int> foo = new Dictionary<string, int>();
     private static ReaderWriterLock rwLock= new ReaderWriterLock();
     static int reads = 0;
     static int writes = 0;


     private static bool Contains(string bar) 
     {
          try 
          { 
              rwLock.AquireReaderLock(TimeOut.Infinite);
              InterLocked.Increment(ref reads);
              return foo.ContainsKey(bar);


          }
          catch(Exception) 
          {


          }
          finally 
          {
                rwLock.ReleaseReaderLock();
          }
     }

     public static void Add(string bar)
     {
        try 
        {
           rwLock.AquireWriterLock(TimeOut.Infinite);

           if (!ContainsKey(bar)) 
           {
                 foo.Add(bar, 0);
           }
           foo[bar] = foo[bar] + 1;
           Interlocked.Increment(ref writes);
        }
        catch(Exception) {}
        finally 
        {
              rwLock.ReleaseWriterLock();
        }
private static Dictionary foo=new Dictionary();
私有静态ReaderWriterLock rBlock=新的ReaderWriterLock();
静态int读取=0;
静态int写入=0;
私有静态bool包含(字符串栏)
{
尝试
{ 
rwLock.AquireReaderLock(TimeOut.Infinite);
联锁增量(参考读数);
返回食品容器(巴);
}
捕获(例外)
{
}
最后
{
rBlock.ReleaseReaderLock();
}
}
公共静态无效添加(字符串栏)
{
尝试
{
rwLock.AquireWriterLock(TimeOut.Infinite);
如果(!ContainsKey(bar))
{
foo.Add(bar,0);
}
foo[bar]=foo[bar]+1;
联锁增量(ref写入);
}
捕获(异常){}
最后
{
rBlock.ReleaseWriterLock();
}
}


您也可以对remove执行同样的操作。

我确实在使用.Net 4.0。我将看看System.Collections.Concurrent提供了什么。由于我使用的是.NET 4.0,大家一致认为我应该用ConcurrentDictionary替换我的字典。这看起来很好。但是,如何避免上述情况?是否甚至可以使用Interlocked.Increment(ref foo[bar]),它不会给您一个编译错误,说明它在索引器或属性上不起作用?
     private static Dictionary<string, int> foo = new Dictionary<string, int>();
     private static ReaderWriterLock rwLock= new ReaderWriterLock();
     static int reads = 0;
     static int writes = 0;


     private static bool Contains(string bar) 
     {
          try 
          { 
              rwLock.AquireReaderLock(TimeOut.Infinite);
              InterLocked.Increment(ref reads);
              return foo.ContainsKey(bar);


          }
          catch(Exception) 
          {


          }
          finally 
          {
                rwLock.ReleaseReaderLock();
          }
     }

     public static void Add(string bar)
     {
        try 
        {
           rwLock.AquireWriterLock(TimeOut.Infinite);

           if (!ContainsKey(bar)) 
           {
                 foo.Add(bar, 0);
           }
           foo[bar] = foo[bar] + 1;
           Interlocked.Increment(ref writes);
        }
        catch(Exception) {}
        finally 
        {
              rwLock.ReleaseWriterLock();
        }