从库中捕获主线程SynchronizationContext或Dispatcher [英] Capturing the main thread SynchronizationContext or Dispatcher from a library

查看:105
本文介绍了从库中捕获主线程SynchronizationContext或Dispatcher的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C#库,希望能够将工作发送/发布到主" ui线程(如果存在). 该库可用于:

I have a C# library that would like to have the ability to Send/Post work to the "main" ui thread (if one exists). This library may be used by:

  • winforms应用程序
  • 本机应用程序(带有UI)
  • 控制台应用程序(无UI)

在库中,我想在初始化期间捕获某些东西(一个SynchronizationContext,一个Dispatcher,一个Task Scheduler或其他东西),这将使我能够(稍后)将工作发送/发布到主线程(如果主线程具有该功能-即它具有消息泵).例如,当且仅当主应用程序能够让我进入主线程时,该库才会在主线程上放置一些Winforms UI.

In the library I'd like to capture something (A SynchronizationContext, a Dispatcher, a Task Scheduler, or something else) during initialization, that will allow me to (at a later time) Send/Post work to the main thread (if the main thread has that ability--i.e. it has a message pump). For example, the library would like to put up some Winforms UI on the main thread if and only if the main application has the ability for me to get to the main thread.

我尝试过的事情:

  1. SynchronizationContext : 对于Winforms应用程序来说,捕获此文件效果很好(将安装 WindowsFormsSynchronizationContext 作为当前 SynchronizationContext.对于控制台应用程序- -因为我可以检测到Current SynchronizationContext为空(因此,我知道我没有向主线程发送/发送工作的能力),所以这里的问题是本机UI应用程序:它具有该能力(即(它有一个消息泵),但是Current Synchronization上下文为null,因此我无法将其与Console应用程序的情况区分开.如果可以区分,那么我可以在主线程上简单地安装WindowsFormsSynchronizationContext,我很好去.
  2. A 调度程序:使用 Dispatcher.FromThread (不会创建Dispatcher)如果该线程不存在,则为该线程).但是本机UI应用程序将使用此方法返回一个空的Dispatcher,因此,我再次陷入无法将UI应用程序与控制台应用程序区分开的境地.
  3. 一个 TaskScheduler :我可以使用FromCurrentSynchronizationContext .这具有与SynchronizationContext相同的问题. IE.在调用FromCurrentSyncronizationContext之前,我必须检查Current SynchronizationContext是否为null(对于Console应用程序和本机ui应用程序就是这种情况).因此,我再也无法将本机ui应用程序与控制台应用程序区分开来.
  1. A SynchronizationContext: Capturing this works fine for a Winforms application (a WindowsFormsSynchronizationContext will be installed as the Current SynchronizationContext. This also works fine for the console app--since I can detect that the Current SynchronizationContext is null (and thus, know that I don't have the ability to send/post work to the main thread). The problem here is the native UI application: It has the ability (i.e. it has a message pump), but the Current Synchronization context is null and thus I can't differentiate it from the Console app case. If I could differentiate, then I could simply install a WindowsFormsSynchronizationContext on the main thread, and I'm good to go.
  2. A Dispatcher: Capturing this using Current creates a new SynchronizationContext. Thus, in all situations I will get back a Dispatcher. However, for a Console app, using Dispatcher.Invoke from a background thread will hang (as expected). I could use Dispatcher.FromThread (which doesn't create a Dispatcher for the thread if one doesn't exist). But the native UI application will return a null Dispatcher using this method, and so then I'm, again, stuck not being able to distinguish the UI application from the console application.
  3. A TaskScheduler: I could use FromCurrentSynchronizationContext. This has the same problems as the SynchronizationContext. I.e. Before calling FromCurrentSyncronizationContext, I'd have to check if the Current SynchronizationContext is null (which will be the case for the Console app and the native ui application). So, again I can't distinguish the native ui application from the console application.

我当然可以让我的图书馆用户在调用我的Initialize方法时指定它是否是UI应用程序,但是我希望尽可能避免图书馆用户的麻烦.

I, of course, could have the user of my library specify whether or not it is a UI application when they call my Initialize method, but I was hoping to avoid that complication for the user of the library if possible.

推荐答案

通常这是不可能的,易于在线程中使用的库不能对UI线程是哪个特定线程做出任何假设.您可以捕获Synchronization.Current,但是只有在从UI线程调用初始化方法的情况下,它才能正常工作.像TaskScheduler.FromCurrentSynchronizationContext()这样正常工作并不是很不正常的事情,但它往往是偶然的,但不能保证.您可以添加一个检查,如果Thread.CurrentThread.GetApartmentState()不返回STA,则未从UI线程调用您的几率很高.在这种情况下,SynchronizationContext.Current通常也将为null,这是另一种检查方式.

This is not in general possible, a library that's apt to be used in threads cannot make any assumptions about which particular thread is the UI thread. You can capture Synchronization.Current but that will only work correctly if your initialization method is called from the UI thread. That's not terribly unusual to work out well, like TaskScheduler.FromCurrentSynchronizationContext() tends to work by accident, but not a guarantee. You can add a check, if Thread.CurrentThread.GetApartmentState() doesn't return STA then the odds that you are not being called from the UI thread are very high. SynchronizationContext.Current will also often be null in that case, another way to check.

(可以说)更好的方法是不担心它,让客户端代码找出来,将回调整理起来不会有任何麻烦.或公开类型为SynchronizationContext的属性,以便客户端代码可以对其进行分配.或将其添加为构造函数参数.如果准备发布,则抛出InvalidOperationException,但发现它仍然为null,这是客户程序员仅做一次的疏忽.

The (arguably) better ways are to just not worry about it and let the client code figure it out, it won't have any trouble marshaling the callback. Or to expose a property of type SynchronizationContext so that the client code can assign it. Or add it as a constructor argument. Throw an InvalidOperationException if you are ready to Post but find out that it is still null, that's an oversight that the client programmer only makes once.

这篇关于从库中捕获主线程SynchronizationContext或Dispatcher的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆