SEHE调用DeleteTimerQueueTimer时出现异常
本文关键字:异常 调用 DeleteTimerQueueTimer SEHE | 更新日期: 2023-09-27 17:57:52
我使用的是一个Timer类,它封装了CreateTimerQueueTimer
和DeleteTimerQueueTimer
。
这是类别:
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
方法应该防止DeleteTimerQueueTimer在调用Stop()
之后被调用一次,对吧?即使从不同的线程同时调用Stop()
?
SEHException
正在向DeleteTimerQueueTimer()
抛出;我想这是因为它试图删除一个已经删除的计时器,从而使句柄无效。CompareExchange
难道不能防止DeleteTimerQueueTimer
被调用不止一次吗,即使是被多个线程同时调用?
函数InterlockedCompareExchange防止变量"started"同时从2个线程修改,但计时器的句柄是您想要保护的真正句柄,但在某些情况下代码无法做到这一点。
例如,线程A调用start函数,它执行函数Interlocked.CompreExchange,然后this.started为1;此时线程A调用stop函数,它看到'started'是一个,所以它会调用函数DeleteTimerQueueTimer来删除计时器,而计时器可能还没有创建,句柄无效。
因此,您应该保护计时器的句柄