我有一个静态计时器类,任何网页都会调用它来计算每个页面的构建时间。
我的问题是静态类线程安全吗?在我的示例中,并发用户会导致启动和停止时间出现问题吗?例如,不同的线程覆盖我的开始和停止值。
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主页调用。)
答案 0 :(得分:65)
静态方法本身不是 线程安全的。 CLR与实例方法的处理方式没有区别。不同之处在于,通常应该尝试使线程安全。 (我想不出任何不是线程安全的.NET BCL静态方法。)实例方法通常不是线程安全的,因为典型的模式是创建一个对象并从一个线程重复使用它,如果它 必须从多个线程中使用,所涉及的协调包括确保安全地使用该对象。在很多情况下,在协调代码中比在对象本身中更合适。 (通常你想要使整个操作序列有效地成为原子 - 这是在对象内部无法完成的。)
你的Timer
类绝对不是线程安全的:两个线程可以轻松地踩在彼此的数据上,并且在计算持续时间时没有什么可以阻止线程使用“陈旧”数据。
使用Stopwatch
课程 - 这就是它的用途。不可否认,如果你想使用多个线程中的一个实例,你需要采取正常的步骤来确保安全,但总的来说你会处于更好的位置。诚然,Stopwatch
远非完美 - 请参阅this question以及下面的评论以获取更多详细信息 - 但它至少是该类型的设计目标。 (谁知道,它可能会被修复一段时间......)
答案 1 :(得分:22)
有一个很好的讨论here,它更多地关注为什么你的例子不是线程安全的机制和原因。
总而言之,首先,您的静态变量将被共享。如果你可以使它们成为局部变量,即使它们是静态方法的本地变量,它们仍然会得到自己的堆栈帧,因此是线程安全的。此外,如果您以其他方式保护静态变量(即,此线程中其他人提到的锁和/或其他多线程编程技术),您还可以使示例静态类是线程安全的。
其次,因为您的示例不接受您修改的外部变量实例或其状态可能被另一个线程操作,所以您的示例在这方面也是线程安全的。
答案 2 :(得分:20)
您的计时器类绝对不是线程安全的。你应该创建一个普通的类,并在每次需要测量时间时实例化它:
Timer timer = new Timer();
timer.Start();
//...
timer.Stop();
decimal duration = timer.Duration();
更好的是,有一个内置的.NET类正是这样做的:
Stopwatch sw = Stopwatch.StartNew();
sw.Stop();
TimeSpan duration = sw.Elapsed;
答案 3 :(得分:5)
是的,你是对的,这个类的静态成员/访问者会导致它们被不同的用户覆盖。
这就是您拥有实例和非静态成员的原因。