【问题标题】:TimeSetEvent failing after a few seconds (NullReferenceException)几秒钟后 TimeSetEvent 失败 (NullReferenceException)
【发布时间】:2015-12-02 09:08:15
【问题描述】:

我正在使用 TimeSetEvent,它的回调函数正在工作,但几秒钟后,即使回调函数根本不起作用,它也会失败:

// Vars
private TimerEventHandler timerRef;
private uint timerId = 0;

//Later, where I use TimeSetEvent
timerRef = new TimerEventHandler(CallbackFunction);
timerId = timeSetEvent(200, 10, timerRef, UIntPtr.Zero, TIME_KILL_SYNCHRONOUS | TIME_PERIODIC);

即使有 200 毫秒的延迟,它也无法正常工作。

private void CallbackFunction(uint id, uint msg, UIntPtr userCtx, UIntPtr uIntPtr, UIntPtr intPtr)
{

// Even if this is empty, it will fail
}

我要么得到 NullReferenceException(大多数时候)要么得到 AccessViolationException(偶尔)。我怀疑两者都来自同一个问题。

有趣的是,我在另一个班级有完全相同的结构并且它可以工作。我复制了那个类,在这里......它没有。我收到此错误。

我不明白为什么它不起作用(在其他类中它起作用)以及如何解决它。

PD:timerId 返回一个不同于 0 的整数。我不明白这个 null 来自哪里,如果我评论 TimerId = TimeSetEvent... 代码不会失败。

【问题讨论】:

  • 在哪一行代码中抛出异常?
  • 它没有说,因此我找不到它。但是,我注释了所有代码并逐个添加函数,并意识到当我调用此 TimeSetEvent 时,它会在 20 次左右迭代后很快崩溃。即使该功能什么也不做。但是,不称其为“有效”(= 不会崩溃,但显然我需要那个计时器)。
  • timeSetEvent 是做什么的?
  • 类似于 Timers.Timer 但更精确。它被称为 Timer 多媒体,在高达 1ms 的窗口中具有令人难以置信的高精度)。
  • 如果您不向我们显示minimal reproducible example,我们将无法回答。我怀疑这个问题是由一个被 gc-ed 或移动的托管对象引起的。考虑将 TimerEventhandler 保留在范围内,并可能将其包装在 fixed 语句中以防止它在内存中移动。 stackoverflow.com/questions/2490912/what-are-pinned-objects

标签: c# timer callback nullreferenceexception


【解决方案1】:

在与我的同事审查越来越多的代码几天后,我们发现了问题。不得不说这个问题没有足够的信息来解决它,但我们无法知道。这是一个非常复杂的程序,我们无法想象我们需要其他类的代码。

上面的代码在Class B。这是从Class A 调用的。原来Class A 偶尔会被破坏,而TimeSetEventpinvoked(非托管),它继续工作。因此,当它尝试查找 回调函数 时,它已连同来自 Class B 的所有内容一起被释放,并给出了 null 引用异常

解决方案是在Class B 中实现一个方法,在删除Class A 之前终止计时器。然后,删除Class A 是安全的,Class B 不会导致应用崩溃。

【讨论】:

  • 调试器通常总是为此生成a diagnostic。对调试器设置进行彻底审查可能是合适的。正确的解决方案是 GCHandle.Alloc()
  • 嘿@HansPassant,你能提供一些关于 GCHandle.Alloc() 的示例或更多上下文吗? TimerEventHandler 是一个代表,从我能读到的 (manski.net/2012/06/pinvoke-tutorial-pinning-part-4) 他们无法固定。 还要注意,您不需要(实际上也不能)固定代表。但是,您需要延长委托的生命周期,只要它可以从非托管代码中调用。 也不知道如何检查诊断。
  • 与固定无关,您通过 GCHandleType.Normal。这样 GC 总是看到一个对象引用并且不会收集它。
  • 有趣。如果我实施的 Dispose 失败,将检查如何做!
猜你喜欢
  • 1970-01-01
  • 2018-09-27
  • 2021-05-12
  • 2013-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多