我正在使用包裹CreateTimerQueueTimer
和DeleteTimerQueueTimer
的Timer类。
这是班级:
using System;
using System.Threading;
using MyCompany.Internal;
using TimerCallback = MyCompany.Internal.TimerCallback;
public class Timer : IDisposable
{
public Timer()
{
this.callback = this.ticked;
this.autoReset = true;
Computer.ChangeTimerResolutionTo(1);
this.priority = ThreadPriority.Normal;
}
public virtual event EventHandler Elapsed;
public virtual bool AutoReset
{
get
{
return this.autoReset;
}
set
{
this.autoReset = value;
}
}
public virtual ThreadPriority Priority
{
get
{
return this.priority;
}
set
{
this.priority = value;
}
}
public virtual void Start(int interval)
{
if (interval < 1)
{
throw new ArgumentOutOfRangeException("interval", "Interval must be at least 1 millisecond.");
}
if (Interlocked.CompareExchange(ref this.started, 1, 0) == 1)
{
return;
}
NativeMethods.CreateTimerQueueTimer(
out this.handle,
IntPtr.Zero,
this.callback,
IntPtr.Zero,
(uint)interval,
(uint)interval,
CallbackOptions.ExecuteInTimerThread);
}
public virtual void Stop()
{
if (Interlocked.CompareExchange(ref this.started, 0, 1) == 0)
{
return;
}
NativeMethods.DeleteTimerQueueTimer(IntPtr.Zero, this.handle, IntPtr.Zero);
}
public virtual void Dispose()
{
this.Stop();
}
private void ticked(IntPtr parameterPointer, bool unused)
{
if (!this.AutoReset)
{
this.Stop();
}
Thread.CurrentThread.Priority = this.Priority;
var elapsed = this.Elapsed;
if (elapsed != null)
{
elapsed(this, EventArgs.Empty);
}
}
private int started;
private IntPtr handle;
private volatile bool autoReset;
private ThreadPriority priority;
private readonly TimerCallback callback;
}
问题是,在一段时间后,当我从多个线程同时调用Start和Stop时,我遇到了SEHException。 Interlocked.CompareExchange
方法应该阻止在调用Stop()
后调用DeleteTimerQueueTimer,对吧?即使从不同的线程同时调用Stop()
?
SEHException
被DeleteTimerQueueTimer()
抛出;我认为这是因为它试图删除已经删除的计时器,使得句柄无效。 CompareExchange
是否阻止DeleteTimerQueueTimer
被多次调用,即使多个线程同时被调用?
答案 0 :(得分:1)
Interlocked.CompareExchange函数阻止变量&#39; start&#39;从2个线程同时修改,但定时器的句柄是你想要保护的真实的,但代码在某些情况下无法做到。
例如,线程A调用start函数,它执行函数Interlocked.CompareExchange然后this.started为1;此时线程A调用停止功能,它看到&#39;已启动&#39;是一个,所以它将调用函数DeleteTimerQueueTimer来删除定时器,而定时器可能尚未创建,并且句柄无效。
所以你应该保护计时器的句柄