我有一个Elapsed方法,我有一个while循环。如果从另一个线程禁用/停止计时器,我希望此循环停止。我可以依赖Elapsed方法中的计时器的Enabled属性,或者我应该创建一个“volatile bool timerEnabled”变量来确定。我的测试显示它没问题,但我想在投入生产之前确定这一点。
这就是我想要实现的目标(不是实际代码,而是关闭)
private volatile bool isElapsedAlreadyRunning
void myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (!isElapsedAlreadyRunning) // to prevent reentrance
{
isElapsedAlreadyRunning = true;
try
{
while (myTimer.Enabled && some other condition)
{
do stuff
}
}
finally
{
isElapsedAlreadyRunning = false;
}
}
}
myTimer.Start() and myTimer.Stop() are in other methods that can be called frrom other threads
我正在使用System.Timers.Timer类
如果您对此设计有任何其他意见或看到任何陷阱,请随时发表评论:)
由于
编辑:
男人,线程很难。基于答案和其他stackoverflow问题(this answer particularly),这将是这样做的方式(我希望这次没关系)
public class NoLockTimer : IDisposable
{
private readonly System.Timers.Timer _timer;
private bool _isTimerStopped = false;
private readonly object _isTimerStoppedLock = new object();
public NoLockTimer()
{
_timer = new System.Timers.Timer { AutoReset = false, Interval = 1000 };
_timer.Elapsed += delegate
{
try
{
while (!IsTimerStopped && some other condition)
{
// do stuff
}
}
catch (Exception e)
{
// Do some logging
}
finally
{
if (!IsTimerStopped)
{
_timer.Start(); // <- Manual restart.
}
}
};
_timer.Start();
}
public void Stop()
{
IsTimerStopped = true;
if (_timer != null)
{
_timer.Stop();
}
}
private bool IsTimerStopped
{
get
{
lock (_isTimerStoppedLock)
{
return _isTimerStopped;
}
}
set
{
lock (_isTimerStoppedLock)
{
_isTimerStopped = value;
}
}
}
public void Dispose()
{
Stop();
if (_timer != null)
{
_timer.Dispose();
}
}
}
答案 0 :(得分:6)
不,这不安全。在线程池线程上调用Elapsed事件处理程序。您无法预测该线程何时实际调用您的方法,它取决于该进程中正在运行的其他TP线程。在技术上可以同时在飞行中进行两次呼叫。这使得isElapsedAlreadyRunning变量上的 volatile 关键字不足以确保该方法是线程安全的,您必须使用 lock 关键字或Monitor.TryEnter()代替
将Timer的AutoReset属性设置为false时,此问题消失。确保在finally块中重新启动计时器,Timer.Elapsed事件的另一个令人讨厌的问题是在没有诊断的情况下吞没异常。 System.Threading.Timer是一个全能的更好的计时器,没有像这样的惊喜。
Timer.Enabled属性有类似的问题,你总会看到它迟到。
答案 1 :(得分:1)
isElapsedAlreadyRunning
的警卫显然不是线程安全的。
但您可以简单地用lock(...) { ...}
语句替换它。