当两个线程同时访问时,跨线程InvalidOperationException,InvokeRequired==fals

本文关键字:线程 InvalidOperationException fals InvokeRequired 访问 两个 | 更新日期: 2023-09-27 18:00:18

一个控件由两个工作线程访问,第二个工作线程在第一个工作线程完成它在控件上的工作之前访问。第二个线程(9)得到InvokeRequired=false,然后第一个线程(17)在调用子控件上的.Refresh时得到异常。

这是预期的行为吗?究竟是什么导致线程将控件的InvokeRequired视为true/false
最后,什么是好的解决方案。。锁定所有invoke语句,并让它们调用一个单独的方法(显然是为了避免死锁)?

private void OnHistoryUpdate(object sender)
{
    Console.WriteLine("<< Invoke? " + this.InvokeRequired   + " " + Thread.CurrentThread.ManagedThreadId );
    if (this.InvokeRequired)
        this.Invoke(new Action<object>(OnHistoryUpdate), sender);  
    LoadTimeSeries(this.Interval);
    Console.WriteLine(">> Invoke? " + this.InvokeRequired   + " " + Thread.CurrentThread.ManagedThreadId);
}

输出:

<< Invoke? True Thread: 17
<< Invoke? False Thread:  9
>> Invoke? False Thread:  9

当两个线程同时访问时,跨线程InvalidOperationException,InvokeRequired==fals

您的代码更新控件两次:

  • 使用对Invoke的调用,该调用将另一个调用委派给OnHistoryUpdate。这是安全的,因为Invoke将执行封送至UI线程
  • 在呼叫InvokeInvoke同步执行,当它返回时,您的代码将第二次调用LoadTimeSeries,这一次是不安全的(它不会再次检查InvokeRequired

我会按照下面的方式更改方法,并考虑使用BeginInvoke,这样工作线程就不会阻塞。

private void OnHistoryUpdate(object sender)
{
    Console.WriteLine("<< Invoke? " + this.InvokeRequired   + " " + Thread.CurrentThread.ManagedThreadId );
    if (this.InvokeRequired)
        this.Invoke(new Action<object>(OnHistoryUpdate), sender);
    else  
        LoadTimeSeries(this.Interval);
    Console.WriteLine(">> Invoke? " + this.InvokeRequired   + " " + Thread.CurrentThread.ManagedThreadId);    
}

如果在不使用invoke的情况下对需要从线程调用的控件调用Refresh,则会得到异常。确保你不这样做,你会没事的。这就是我们所需要的。