C# 从库中捕获主线程SynchronizationContext或Dispatcher

C# 从库中捕获主线程SynchronizationContext或Dispatcher,c#,winforms,multithreading,synchronization,C#,Winforms,Multithreading,Synchronization,我有一个C#库,希望能够将工作发送/发布到“主”ui线程(如果存在)。 此库可由以下人员使用: winforms应用程序 本机应用程序(带UI) 控制台应用程序(没有UI) 在库中,我希望在初始化期间捕获一些东西(SynchronizationContext、Dispatcher、Task Scheduler或其他东西),这些东西将允许我(稍后)向主线程发送/发布工作(如果主线程具有这种能力,即它有一个消息泵)。例如,如果且仅当主应用程序能够访问主线程时,库希望在主线程上安装一些Winfor

我有一个C#库,希望能够将工作发送/发布到“主”ui线程(如果存在)。 此库可由以下人员使用:

  • winforms应用程序
  • 本机应用程序(带UI)
  • 控制台应用程序(没有UI)
在库中,我希望在初始化期间捕获一些东西(SynchronizationContext、Dispatcher、Task Scheduler或其他东西),这些东西将允许我(稍后)向主线程发送/发布工作(如果主线程具有这种能力,即它有一个消息泵)。例如,如果且仅当主应用程序能够访问主线程时,库希望在主线程上安装一些Winforms UI

我尝试过的事情:

  • A: 捕获它对于Winforms应用程序很好(a将作为SynchronizationContext安装。这对于console应用程序也很好——因为我可以检测到当前SynchronizationContext为null(因此,我不具备向主线程发送/发布工作的能力)。这里的问题是本机UI应用程序:它有这个功能(即,它有一个消息泵),但当前的同步上下文为空,因此我无法将其与控制台应用程序区分开来。如果我可以区分,那么我只需在主线程上安装WindowsFormsSynchronizationContext,就可以了
  • 答:使用捕获会创建一个新的SynchronizationContext。因此,在所有情况下,我都会返回一个调度程序。但是,对于控制台应用程序,使用
    调度程序。从后台线程调用
    将挂起(如预期的那样)。我可以使用(如果不存在调度程序,则不会为线程创建调度程序).但是本机UI应用程序将使用此方法返回空调度程序,因此我再次无法区分UI应用程序和控制台应用程序
  • 答:我可以使用。这与SynchronizationContext有相同的问题。也就是说,在从CurrentSynchronizationContext调用之前,我必须检查当前SynchronizationContext是否为null(这对于控制台应用程序和本机ui应用程序都是如此)。因此,我再次无法区分本机ui应用程序和控制台应用程序
  • 一、 当然,可以让我的库的用户在调用我的
    Initialize
    方法时指定它是否是UI应用程序,但我希望尽可能避免库的用户出现这种复杂情况。

    我认为您应该将此作为
    Initialize
    方法的一个选项(或者以某种方式允许您的来电者请求UI交互),对我来说,这更有意义。我不知道具体细节,但在我看来这似乎是一种“礼貌”要做的事情是,让你的调用者决定他们是想要你还是想要支持你的UI。我会更进一步,甚至作为调用者提供一个同步上下文。但这是我的观点


    要回答你的问题,有几个“黑客”您可以使用来确定您是否在控制台应用程序中运行。因此,这个问题有一些相关信息:

    这通常是不可能的,易于在线程中使用的库无法对哪个特定线程是UI线程做出任何假设。您可以捕获同步。当前的,但这只能正常工作如果您的初始化方法是从UI线程调用的,则不会发生异常情况。这与TaskScheduler一样正常工作。FromCurrentSynchronizationContext()往往是偶然工作的,但不是保证。如果thread.CurrentThread.GetApartmentState()如果不返回STA,则您没有从UI线程被调用的可能性非常高。SynchronizationContext.Current在这种情况下也通常为null,这是另一种检查方法


    (有争议的)问题更好的方法是不用担心它,让客户机代码解决它,它将不会在封送回调时遇到任何问题。或者公开SynchronizationContext类型的属性,以便客户机代码可以分配它。或者将其添加为构造函数参数。如果准备发布但发现它仍然为null,这是客户端程序员只做了一次的疏忽。

    更改库初始化以使用SyncronizationContext参数。如果该参数为null,则库不需要执行任何特殊操作,如果不是null,则在那里发布/发送GUI更新。

    我认为这正是所要做的。状态:

    基于事件的异步模式提供了打包具有异步功能的类的标准化方法。如果使用帮助器类(如)实现,则您的类将在任何应用程序模型下正常工作,包括ASP.NET、控制台应用程序和Windows窗体应用程序

    由调用者决定是否要在UI线程上调用您的API。如果他们这样做,这将捕获上下文,事件将按顺序通过消息泵。在控制台应用程序中,如果您安装SynchronizationContext,则可以获得相同的行为,您可以使用从
    Nito.AsyncEx
    nuget软件包。无需额外属性,也无需自己编写条件代码。如果没有可用的序列化同步上下文,则将使用控制台应用程序可用的假同步上下文,而控制台应用程序只是将事件排队到线程池(这意味着帖子可能不会按顺序执行)。只需记住在完成时调用
    AsyncOperation.OperationCompleted()
    AsyncOperation.PostOperationCompleted()

    在库中,我希望在初始化期间捕获一些东西(SynchronizationContext、Dispatcher、Task Scheduler或其他东西)

    这正是我们所要做的,而且是以一种环境不可知的方式