SEHE调用DeleteTimerQueueTimer时出现异常

本文关键字:异常 调用 DeleteTimerQueueTimer SEHE | 更新日期: 2023-09-27 17:57:52

我使用的是一个Timer类,它封装了CreateTimerQueueTimerDeleteTimerQueueTimer

这是类别:

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被调用不止一次吗,即使是被多个线程同时调用?

SEHE调用DeleteTimerQueueTimer时出现异常

函数InterlockedCompareExchange防止变量"started"同时从2个线程修改,但计时器的句柄是您想要保护的真正句柄,但在某些情况下代码无法做到这一点。

例如,线程A调用start函数,它执行函数Interlocked.CompreExchange,然后this.started为1;此时线程A调用stop函数,它看到'started'是一个,所以它会调用函数DeleteTimerQueueTimer来删除计时器,而计时器可能还没有创建,句柄无效。

因此,您应该保护计时器的句柄