我们正在尝试从硬件设备读取信息,唯一的方法是通过硬件制造商提供的封闭源本机DLL与其进行通信。它们还提供了一个.NET包装器来访问DLL,下面简化了所关注的包装器方法:
[DllImport("hardware_mfg.dll")]
private static extern int hardware_command_unicode(MarshalAs(UnmanagedType.LPWStr)] string outdata, uint outcount, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder indata, uint maxdata, ref uint incount);
public static int HardwareCommand(string senddata, StringBuilder recdata)
{
uint incount = 0;
return HardwareWrapper.hardware_command_unicode(senddata, (uint)senddata.Length, recdata, (uint)recdata.Capacity, ref incount);
}
调用HardwareWrapper.HardwareCommand函数的代码是:
// This method gets called from a DispatcherTimer.Tick event
public static int SendCommand(string command, StringBuilder buffer)
{
lock (_locker)
{
try
{
if (_commandInProgress)
{
// This exception gets thrown
throw new InvalidOperationException("This should not be possible");
}
_commandInProgress = true;
// InvalidOperationException gets thrown while previous call to HardwareCommand here has not yet returned
var result = HardwareWrapper.HardwareCommand(command, buffer);
return result;
}
finally
{
_commandInProgress = false;
}
}
}
令人困惑的部分是抛出InvalidOperationException。当主线程进入var result = HardwareWrapper.HardwareCommand(...)
时,可以再次调用该方法,并在第一次调用返回之前输入相同的函数。抛出异常是间歇性的,但让这段代码运行15-30秒就足以让异常发生。
编辑1:将锁定移至外部范围
答案 0 :(得分:1)
如果没有良好的Minimal, Complete, and Verifiable code example,就无法提供具体而完整的诊断。但根据这里的信息,毫无疑问,这正是as @David says:"您的非托管代码正在抽取队列" 。
COM尤其因此而臭名昭着,但它可能以其他方式发生。通常,本机代码进入某种等待状态,其中仍然分派线程的一些或所有消息。这可以包括计时器的WM_TIMER消息,导致Tick
事件再次被引发,甚至在前一个事件处理程序返回之前。
由于它位于同一个帖子中,lock
无关紧要。 Monitor
使用的lock
仅阻止线程其他而不是持有锁的线程;当前线程可以根据需要重新输入受该监视器保护的任何代码段。
InvalidOperationException
,&#34中的消息;这不可能" ,不正确。它是可能的,应该是可能的。无论好坏,它都是Windows中消息的工作方式。
根据您的目标和相关代码的具体情况(您尚未提供),您至少有以下几种选择:
不要使用DispatcherTimer
。而是使用其他一个计时器类,它使用线程池来引发计时器事件。这些不依赖于消息队列,因此泵送消息不会影响定时器事件的引发方式。当然,这假设您不需要在UI线程中执行代码。在这种情况下是否属于这种情况,问题并不明确。 (实际上,即使你需要在持有lock
的同时在UI线程中执行一些代码,也可以使这种方法起作用,但它变得棘手......如果你能帮助它,最好避免这样做。 )
如果标志已设置为_commandInProgress
,请使用true
变量检测情况并忽略计时器事件。当然,这假设您不需要在每个计时器事件上执行命令,并且有一些合理的方法可以跳过这样做(包括处理缺少调用的结果值)本地代码)。同样,问题中的信息不足以了解是否属于这种情况。