【问题标题】:Making a Dictionary in a static class thread-safe?在静态类中制作字典是线程安全的?
【发布时间】:2016-05-28 04:37:03
【问题描述】:

到目前为止,我一直在 C# 库中开发static class,所有方法显然都是静态的。任何被操作的数据都会被传入,所以这是线程安全的。但是,我正在实现一个功能,该功能需要 Dictionary 来统计某些字符串 ID,这些 ID 将为每个线程生成不同的值。显然使用类似下面的东西不会是线程安全的,因为多个线程会使用相同的Dictionary 并搞乱它的状态:

private static readonly Dictionary<string, uint> MyTallies = new Dictionary<string, uint>();

那么,让这个线程安全的最佳选择是什么?在我看来,有 3 个(有点糟糕)选项:

  1. 使类非静态;每个线程都必须创建一个新实例,并且可以为每个线程创建一个新的Dictionary。性能受到影响。
  2. 使用Dictionary 在代码周围加上lock,并清除锁定末尾的Dictionary。性能大受打击。
  3. Dictionary 传递给每个需要它的方法。然而,这会导致 Dictionary 被传入只是用于大多数时间不会使用的可选功能,因此调用代码必须做的事情似乎很奇怪。由于调用代码每次都必须创建 Dictionary 实例,因此性能也会受到影响。

那么我最好的选择是什么,或者还有其他更好的选择吗?

编辑:一个简单的用例:

internal static class HtmlFormatter {
    private static readonly Dictionary<string, uint> MyTallies = new Dictionary<string, uint>();

    internal static string RenderHtml(string markdown) {
        string outputHtml = "";

        // (process markdown and render to HTML)
        // (found a heading that requires a unique ID)
        var id = generatedId;
        string unique = "";
        if (MyTallies.ContainsKey(id)) {
            // Already exists; suffix "-x" where x is count of duplicate IDs
            unique = "-" + ++MyTallies[id];
        }
        else {
            MyTallies[id] = 0;
        }
        id += unique;

        // (append id string to HTML output and continue processing)

        return outputHtml;
    }
}

【问题讨论】:

  • ConcurrentDictionary?
  • @EldarDordzhiev 只有退化边缘情况下的解决方案 - 任何不完全微不足道的事情都不会适用于该解决方案,并且需要代码级锁定。
  • 不太确定用例是什么。你提到的选项意味着字典并不是一直都需要的。您能否展示一个示例用法,即谁负责清除字典,它是如何填充和使用的。

标签: c# .net multithreading dictionary


【解决方案1】:

锁定对字典的访问。这样,这些方法就不再是多线程的了——一个方法中只有一个。查找 lock 语句。

如果您更喜欢,请使用手动锁定 - 如果大多数访问是只读的,则多个读取器单个写入器可能会提高性能。它允许多个读取器锁定,但一次只允许一个写入器。

3 根本不是解决方案——因为字典不是线程安全的。除非您每次都使用 ConcurrentDictionary 或制作副本。

1 不是解决方案,因为它是架构更改。它可能会起作用 - 但为什么它首先是静态的?有一个原因,您要求我们在没有适当知识的情况下提供建议。

【讨论】:

    【解决方案2】:

    感觉就像是在冒着编写过于复杂的代码的风险进行预优化。我建议您编写最简单最可靠的代码,直到您准确测试性能瓶颈所在。

    除非确实需要,否则最好避免在线程之间共享对象或状态。

    使类非静态;每个线程都必须创建一个新实例, 并且可以为每个线程创建一个新的字典。性能受到影响。

    这是我推荐的方式。字典的一些额外字节可以忽略不计,甚至可以更快地使用,因为每个字典中的项目数会更少。

    将 Dictionary 传递给每个需要它的方法。然而 这导致字典被传入只是一个可选的 大多数时候不会使用的功能,所以它看起来像一个 调用代码必须做的奇怪的事情。性能也受到打击 因为调用代码每次都必须创建 Dictionary 实例。

    如果不查看您的代码,我无法判断,但感觉您的代码做得太多了。如果您考虑传入可能不会使用的变量,那么您可能需要将代码分解为更小的组件。 (见Single Responsibility Principle

    如果最终证明您确实需要在线程之间共享对象,那么您可以使用多个helpful patterns 和诸如 ConcurrentDictionary 之类的类。 (感谢埃尔达尔·多尔吉耶夫)

    【讨论】:

      猜你喜欢
      • 2012-09-22
      • 2015-03-24
      • 1970-01-01
      • 2013-02-10
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多