检测是否在WPF和Winforms中的UI线程上

chi*_*tom 42 c# wpf assertions winforms

我在下面编写了一个断言方法Ensure.CurrentlyOnUiThread(),用于检查当前线程是否为UI线程.

  • 这在检测Winforms UI线程时是否可靠?
  • 我们的应用程序是混合WPF和Winforms,如何最好地检测有效的WPF UI线程?
  • 有一个更好的方法吗?也许代码合同?

Ensure.cs

using System.Diagnostics;
using System.Windows.Forms;

public static class Ensure
{
    [Conditional("DEBUG")]
    public static void CurrentlyOnUiThread()
    {
        if (!Application.MessageLoop)
        {
            throw new ThreadStateException("Assertion failed: not on the UI thread");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Cod*_*key 52

不要用

if(Dispatcher.CurrentDispatcher.Thread == Thread.CurrentThread)
{
   // Do something
}
Run Code Online (Sandbox Code Playgroud)

Dispatcher.CurrentDispatcher将,如果当前线程没有调度程序,则创建并返回Dispatcher与当前线程关联的新线程.

相反,这样做

Dispatcher dispatcher = Dispatcher.FromThread(Thread.CurrentThread);
if (dispatcher != null)
{
   // We know the thread have a dispatcher that we can use.
}
Run Code Online (Sandbox Code Playgroud)

为了确保您拥有正确的调度程序或在正确的线程上,您有以下选项

Dispatcher _myDispatcher;

public void UnknownThreadCalling()
{
    if (_myDispatcher.CheckAccess())
    {
        // Calling thread is associated with the Dispatcher
    }

    try
    {
        _myDispatcher.VerifyAccess();

        // Calling thread is associated with the Dispatcher
    }
    catch (InvalidOperationException)
    {
        // Thread can't use dispatcher
    }
}
Run Code Online (Sandbox Code Playgroud)

CheckAccess()并且VerifyAccess()不会出现在intellisense中.

此外,如果你不得不诉诸这些东西,可能是由于糟糕的设计.您应该知道哪些线程运行程序中的代码.

  • 我已经尝试了所提供的解决方案,但得出结论它不能从Task.Run()初始化的后台线程工作.这里提供的解决方案运行良好:http://stackoverflow.com/a/13726324/249948. (5认同)
  • 请注意,Herman Cordes 链接的解决方案适用于 WPF。对于 WinForms,请使用 CodeMonkey 的答案,或者简单地执行 `if (System.Windows.Forms.Application.MessageLoop)`。 (2认同)

Ian*_*Ian 20

在WinForms中你通常会使用

if(control.InvokeRequired) 
{
 // Do non UI thread stuff
}
Run Code Online (Sandbox Code Playgroud)

对于WPF

if (!control.Dispatcher.CheckAccess())
{
  // Do non UI Thread stuff
}
Run Code Online (Sandbox Code Playgroud)

我可能会写一个使用Generic约束的方法来确定你应该调用哪些.例如

public static bool CurrentlyOnUiThread<T>(T control)
{ 
   if(T is System.Windows.Forms.Control)
   {
      System.Windows.Forms.Control c = control as System.Windows.Forms.Control;
      return !c.InvokeRequired;
   }
   else if(T is System.Windows.Controls.Control)
   {
      System.Windows.Controls.Control c = control as System.Windows.Control.Control;
      return c.Dispatcher.CheckAccess()
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢这些,我发现 InvokeRequired 非常不可靠,特别是如果控件还没有句柄或正在处理,我也想要一个不需要访问控件的断言。 (2认同)

Mat*_*ský 15

对于WPF:

// You are on WPF UI thread!
if (Thread.CurrentThread == System.Windows.Threading.Dispatcher.CurrentDispatcher.Thread)
Run Code Online (Sandbox Code Playgroud)

对于WinForms:

// You are NOT on WinForms UI thread for this control!
if (someControlOrWindow.InvokeRequired)
Run Code Online (Sandbox Code Playgroud)

  • 关于WPF,这个答案是不正确的.请记住,每个线程都有一个调度员.所以基本上在你的语句中你要问"这个线程是否等于与该线程的调度程序相关联的线程?" 或者,压缩一点,你问"这个线程是否等于这个线程?" 嗯,当然是的.请参阅此处的MS文档:http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.checkaccess.aspx#feedback (7认同)
  • WPF中的每个线程都没有调度程序!但他们可以得到一个!如果当前线程没有,Dispatcher.CurrentDispatcher将创建并返回一个新的Dispatcher! (4认同)
  • 如果在希望检查的UI线程上创建了"someControlorWindow",则后一种情况才有效.拥有多个Windows UI线程完全"没问题". (2认同)
  • @pst - 可能没问题,但很少见.几乎在所有情况下,WPF和WinForms应用程序都只有一个UI线程. (2认同)
  • @Goran - 你说的是真的,但你误解了Sam的观点.对CurrentDispatcher的调用永远不会返回一个DifFERENT线程的调度程序而不是Thread.CurrentThread.相反,如果CurrentThread没有调度程序,它将为它创建一个 - 而调度程序的线程将是CurrentThread.所以测试总是评估为`CurrentThread == CurrentThread`,所以`true`.所以这个答案是错误的. (2认同)

Cur*_*tis 11

对于WPF,我使用以下内容:

public static void InvokeIfNecessary (Action action)
{
    if (Thread.CurrentThread == Application.Current.Dispatcher.Thread)
        action ();
    else {
        Application.Current.Dispatcher.Invoke(action);
    }
}
Run Code Online (Sandbox Code Playgroud)

关键是检查Dispatcher.CurrentDispatcher(它将为您提供当前线程的调度程序),您需要检查当前线程是否与应用程序的调度程序或其他控件匹配.

  • 这是唯一可行的解​​决方案.排名靠前的答案通常会返回线程池线程的非空调度程序. (2认同)

Ale*_*x F 6

也许Control.InvokeRequired(WinForms)和Dispatcher.CheckAccess(WPF)对你好吗?