【问题标题】:Are static methods thread safe静态方法线程安全吗
【发布时间】:2010-11-08 14:32:03
【问题描述】:

我有一个静态计时器类,任何网页都会调用它来计算每个页面构建所需的时间。

我的问题是静态类线程安全吗?在我的示例中,并发用户会导致我的开始和停止时间出现问题吗?例如,不同的线程会覆盖我的开始和停止值。

public static class Timer
{
    private static DateTime _startTime;
    private static DateTime _stopTime;    

    /// <summary>
    /// Gets the amount of time taken in milliseconds
    /// </summary>
    /// <returns></returns>
    public static decimal Duration()
    {
        TimeSpan duration =  _stopTime - _startTime;
        return duration.Milliseconds;
    }

    public static void Start()
    {
        _startTime = DateTime.Now;
    }

    public static void Stop()
    {
        _stopTime = DateTime.Now;
    }
}

这个类应该是一个非静态类吗?

(该类将从 asp.net 母版页调用。)

【问题讨论】:

标签: c# asp.net static


【解决方案1】:

是的,你是对的,这个类上的静态成员/访问器会导致它们被不同的用户覆盖。

这就是你有实例和非静态成员的原因。

【讨论】:

    【解决方案2】:

    静态方法不是固有的线程安全的。 CLR 对它们的处理与实例方法没有区别。不同之处在于通常应该尝试使它们线程安全。 (我想不出任何不是线程安全的 .NET BCL 静态方法。)实例方法通常不是线程安全的,因为典型的模式是创建一个对象并从一个线程重复使用它,如果它确实必须在多个线程中使用,所涉及的协调包括确保对象被安全使用。在很多情况下,在协调代码中比在对象本身中更合适。 (通常你想让整个操作序列有效地原子化——这是在对象中无法完成的。)

    您的Timer 类绝对不是线程安全的:两个线程可以轻松地相互踩踏对方的数据,并且没有什么可以阻止线程在计算持续时间时使用“陈旧”数据。

    改用 Stopwatch 类 - 这就是它的用途。诚然,如果您想使用来自多个线程的一个实例,您需要采取正常步骤来确保安全,但总的来说您会处于更好的位置。诚然 Stopwatch 也远非完美 - 请参阅 this question 和下面的评论了解更多详细信息 - 但它至少是该类型的设计目的。 (谁知道呢,过段时间就修好了……)

    【讨论】:

    • 秒表类有它自己的问题,如果你使用多核或多处理器的话。秒表使用滴答计数来确定持续时间,并且由于 BIOS 中的错误,秒表可以在一个内核上启动并在另一个内核上停止,此时两个内核上的滴答计数不同步。我在 Vss2Git 开源应用程序中发现了这一点,该应用程序使用秒表,有时会尝试给出负的持续时间。 Fpr 更多信息见stackoverflow.com/a/7919483/216440
    • @SimonTewsi:是的,我以前听说过。将编辑一个链接到答案。
    • @JonSkeet,你能解释一下“实例方法通常不是线程安全的,因为典型的模式是创建一个对象并从一个线程重复使用它”。这很令人困惑,因为当我听到实例时,我会自动认为线程安全。您上面所说的是否与 Guffa stackoverflow.com/questions/3037637/… 的这篇文章中的声明相同?我想确保我理解你的陈述中所说的内容。谢谢。
    • @Fractal:是的,Guffa 的说法和我说的差不多。
    【解决方案3】:

    您的计时器类绝对不是线程安全的。您应该创建一个普通类并在每次需要测量时间时实例化它:

    Timer timer = new Timer();
    
    timer.Start();
    //...
    timer.Stop();
    
    decimal duration = timer.Duration();
    

    更好的是,有一个内置的 .NET 类可以做到这一点:

    Stopwatch sw = Stopwatch.StartNew();
    
    sw.Stop();
    
    TimeSpan duration = sw.Elapsed;
    

    【讨论】:

      【解决方案4】:

      here 有一个很好的讨论,它更多地关注了您的示例不是线程安全的机制和原因。

      总而言之,首先,您的静态变量将被共享。如果您可以使它们成为局部变量,即使它们是静态方法的局部变量,它们仍将获得自己的堆栈帧,因此是线程安全的。此外,如果您以其他方式保护您的静态变量(即锁和/或本线程中其他人提到的其他多线程编程技术),您还可以使您的示例静态类线程安全。

      其次,因为您的示例不包含您修改的外部变量实例,或者其状态可能被另一个线程执行,所以您的示例在这方面也是线程安全的。

      【讨论】:

      • 很好的信息,这正是我来这里找到的:如果静态方法只使用局部变量,它是线程安全的吗?干杯。
      • 是的。但请记住,它必须是方法的局部变量——而不是静态类变量。 (我认为有时类变量被称为局部变量,但我可能会误解。)既然你说“只使用局部变量”,我也认为这意味着你没有分配传入的引用局部变量的参数。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多