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

2024-02-11

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

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

我一直在阅读联锁类 http://msdn.microsoft.com/en-us/library/system.threading.interlocked.aspx作为提供线程安全的一种手段,看来这可能是我可以使用的东西:

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

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


public static void Remove(string bar)
{
    if (foo.ContainsKey(bar))
    {
        if (foo[bar] > 0)
            Interlocked.Decrement(ref foo[bar]);
    }
}

然而,我的直觉是,当我实际向字典添加新项目时,这不会解决问题,所以我立即想到了锁定:

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

这种方法是理想的还是正确的?可以改进吗?我还差得远吗?


lock是好的(而且是必要的;你怀疑是对的Interlocked还不够)但是一旦你这样做了Interlocked.Increment and Interlocked.Decrement是不必要的。访问问题Dictionary<TKey, TValue>与多个线程相比,一个线程可能会触发内部哈希表的重建,然后该线程在重建过程中被替换为另一个线程,该线程现在出现并添加到字典中,对字典的内部结构造成严重破坏。

此外,您的实施效果很好,因为您lock on a private对象而不是this. As 奇巴城 https://stackoverflow.com/questions/4234049/net4-0-thread-safe-manner-of-updating-a-dictionary-and-its-values/4234088#4234088指出,这lock对象应该是readonly though.

请小心,不要欺骗自己,认为您现在已经使字典在多线程场景中变得防弹了。例如,可能会发生以下情况:

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

线程 1 被交换为线程 2。

线程2用key调用remove"Hello, World!"将计数设置为0.

线程 2 被交换为线程 1。

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

最后,在 .NET 4.0 中,您应该考虑使用ConcurrentDictionary<TKey, TValue> http://msdn.microsoft.com/en-us/library/dd287191.aspx。请注意,这有效地消除了需要lock当在字典上调用实例方法时,但这并不能消除上述情况的发生。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.NET4.0:更新字典及其值的线程安全方式 的相关文章

  • 在 LINQ 查询中返回不带时间的日期

    我正在编写一个查询 我想计算按日期联系我们的呼叫中心的次数 看起来很简单 但由于联系日期字段是日期时间字段 我得到了时间 因此当我按联系日期 时间 分组时 每个联系日期实例的计数为 1 所以 我想只按日期分组 而不按时间分组 下面是我用来查
  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • C++:无法使用scoped_allocator_adaptor传播polymorphic_allocator

    我有一个vector
  • Func 方法参数的首选命名约定是什么?

    我承认这个问题是主观的 但我对社区的观点感兴趣 我有一个缓存类 它采用类型的缓存加载器函数Func
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • iPhone SDK - 在后台线程中运行重复进程

    我有一个iPhone我想在其中每隔一段时间在后台执行一个方法的应用程序1第二 所以在我的主线程中 我有以下代码UIViewController viewDidLoad NSTimer timerWithTimeInterval 1 0 ta
  • Cygwin 下使用 CMake 编译库

    我一直在尝试使用 CMake 来编译 TinyXML 作为一种迷你项目 尝试学习 CMake 作为补充 我试图将其编译成动态库并自行安装 以便它可以工作 到目前为止 我已经设法编译和安装它 但它编译成 dll 和 dll a 让它工作的唯一
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 跨多个控件共享事件处理程序

    在我用 C 编写的 Windows 窗体应用程序中 我有一堆按钮 当用户的鼠标悬停在按钮上时 我希望按钮的边框发生变化 目前我有以下多个实例 每个按钮一个副本 private void btnStopServer MouseEnter ob
  • 写入和读取文本文件 - C# Windows 通用平台应用程序 Windows 10

    有用 但在显示任何内容之前 您必须在文本框中输入内容 我想那是因为我使用了 TextChanged 事件处理程序 如果我希望它在没有用户交互的情况下显示文本文件的内容 我应该使用哪个事件处理程序 因此 我想在按下按钮时将一些数据写入 C W
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • 使用安全函数在 C 中将字符串添加到字符串

    我想将文件名复制到字符串并附加 cpt 但我无法使用安全函数 strcat s 来做到这一点 错误 字符串不是空终止的 我确实设置了 0 如何使用安全函数修复此问题 size strlen locatie size nieuw char m
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • 可空属性与可空局部变量

    我对以下行为感到困惑Nullable types class TestClass public int value 0 TestClass test new TestClass Now Nullable GetUnderlyingType
  • 已过时 - OpenCV 的错误模式

    我正在使用 OpenCV 1 进行一些图像处理 并且对 cvSetErrMode 函数 它是 CxCore 的一部分 感到困惑 OpenCV 具有三种错误模式 叶 调用错误处理程序后 程序终止 Parent 程序没有终止 但错误处理程序被调
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 为什么 strtok 会导致分段错误?

    为什么下面的代码给出了Seg 最后一行有问题吗 char m ReadName printf nRead String s n m Writes OK char token token strtok m 如前所述 读取字符串打印没有问题 但

随机推荐