问题 正确使用(或不使用)Dispatcher.CheckAccess()


在Winforms中,所有控件都有 InvokeRequired 如果我必须调用,则返回true。[开始]调用控件以修改它。

在WPF中,有一个明显相似的结构 DispatcherObject.CheckAccess() 和 Dispatcher.CheckAccess(),但我被吓坏了 EditorBrowsable(EditorBrowsableState.Never) 属性。当我禁用这样的编辑器浏览时,我用它来表示“你应该  这样做。不完全是。如果这是必要的,解决你的问题,你就错误地设计了解决你的首要问题的解决方案。“另一方面,我发现的唯一替代方案(事实上,我的原始解决方案)是 Thread.CurrentThread.ManagedThreadId == 1。 (这太可怕了。它在通用情况下不起作用。我知道。但它确实适用于我的有限用途。)

MSDN  文件 对于背后的存在和推理保持沉默 EditorBrowsable 属性。它是否确实意味着“不要使用它”,如果我输入它,或者它是否有其他一些不那么禁止的含义?


5621
2017-10-17 15:32


起源

来自MSFT的几个人似乎在这篇文章中回复了。 social.msdn.microsoft.com/Forums/en-US/wpf/thread/... 似乎 EditorBrowsableState.Advanced 可能更合适,但似乎没有任何技术原因,为什么不应该调用它(至少从那个线程)。 - keyboardP


答案:


在WPF中,您可以致电 Dispatcher.Invoke 无论你当前的线程如何,它都会相应地处理调用 - 如果你已经在正确的线程上,它只会调用你的代码,它会使用它 CheckAccess 处理这种行为。

为一个 BeginInvoke 你目前使用的主题是无关紧要的: BeginInvoke 始终是异步的,执行顺序取决于您添加到调度程序队列的项目的优先级。

如果您根本不应该使用该方法,那么它将不公开:该属性的意图仅在于将成员隐藏在诸如Intellisense和其他编辑器浏览器之类的机制中。您通常不需要使用 Dispatcher.CheckAccess() 你自己,这可能是为什么它被标记为不可浏览,但这是我们只能猜测的智慧(除非Eric Lippert正在观看;-)

总结:只需打电话 Dispatcher.Invoke 而且不用担心 CheckAccess


15
2017-10-17 15:59



我认为检查线程访问以确定使用哪个是有好处的。在某些情况下,基于CheckAccess进行显式控制或执行特定先决条件是有意义的。但养成它的习惯是不好的,因为它只是重复努力。坦率地说,Dispatcher.cs是我见过的最乱的.NET类之一。 - erodewald
唯一的缺点是,您不得不记住哪些方法可能会更新UI并始终使用 Invoke()在调用它们时,与在一个方法中检查/调用相反,该方法保证它始终有效而不用担心它 - Basic
Dan当你说“...如果你已经在正确的线程上,它只是调用你的代码,并且它使用CheckAccess来处理这种行为时,我不确定你是对的。”如果查看Dispatcher类的源代码,如果DispatcherPriority为Send,它似乎只调用CheckAccess。 IMO,最好为Dispatcher创建一个包装器类,它对单元测试很有用,但是你可以很容易地为每个Invoke调用添加CheckAccess。从主线程调用Dispatcher.Invoke过去已经为我解决了一些问题。 - Zoman
@Zoman:我在上面看到的来源 Invoke 实施就是如果 this.CheckAccess() 返回true,调用的动作/函数立即运行 - 如果不是,则将其编组到正确的线程。特别是在此范围内 if 声明: if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && this.CheckAccess()) 有一条线 callback() (要么 return callback() 在具有返回值的函数的情况下)将执行目标。 - Dan Puzey
@Dan我在评论中确实提到只检查优先级是Send,这是最高优先级。使用该优先级是非常罕见的,除了你提到的if语句之外,我看不到对CheckAccess()的任何相关调用。因此在大多数情况下,不会调用CheckAccess()。请记住,我目前正在使用.NET 4.0,但我还没有检查最新的.NET版本,这可能已经发生了变化。 - Zoman


我想补充一下: 如果你想模仿“If invokeRequired then ......”
我会说:不要用“if Dispatcher.CheckAccess“ 相反,使用:

If Me.Dispatcher.Thread Is System.Threading.Thread.CurrentThread Then
    Label1.Content = value
Else
    Me.Dispatcher.BeginInvoke(New Action(Of String)(AddressOf displaymessage), value)
    Return
End If


我遇到的问题是CheckAccess始终是真的,即使在开始调用之后......

没关系,以下代码也可以正常工作:

   If Me.Dispatcher.CheckAccess Then
    Label1.Content = value 
  Else
    Me.Dispatcher.BeginInvoke(New Action(Of String)(AddressOf displaymessage), value)
    Return
End If

0
2018-01-21 18:33