从库中捕获主线程SynchronizationContext或Dispatcher
我有一个C#库,希望能够发送/发布工作到“主”ui线程(如果存在)。 该库可用于:
- 一个winforms应用程序
- 本机应用程序(使用UI)
- 控制台应用程序(没有UI)
在库中我想在初始化期间捕获一些东西(A SynchronizationContext,Dispatcher,Task Scheduler或其他东西),这将允许我(稍后)发送/发布工作到主线程(如果主线程有这种能力 – 即它有一个消息泵)。 例如,当且仅当主应用程序能够访问主线程时,库才会在主线程上放置一些Winforms UI。
我尝试过的事情:
- 一个SynchronizationContext :捕获它适用于Winforms应用程序( WindowsFormsSynchronizationContext将作为当前 SynchronizationContext安装。这也适用于控制台应用程序 – 因为我可以检测到当前SynchronizationContext为null(因此,知道我不知道)没有能力发送/发布工作到主线程。这里的问题是本机UI应用程序:它有能力(即它有一个消息泵),但当前同步上下文为空,因此我可以’将它与Console应用案例区分开来。如果我可以区分,那么我可以在主线程上安装一个WindowsFormsSynchronizationContext,我很高兴。
- Dispatcher :使用Current捕获它会创建一个新的SynchronizationContext。 因此,在所有情况下,我都会找回Dispatcher。 但是,对于Console应用程序,从后台线程使用
Dispatcher.Invoke
将挂起(如预期的那样)。 我可以使用Dispatcher.FromThread
(如果不存在,则不会为该线程创建Dispatcher)。 但是本机UI应用程序将使用此方法返回null Dispatcher,因此我再次陷入无法区分UI应用程序与控制台应用程序的问题。 - TaskScheduler :我可以使用FromCurrentSynchronizationContext 。 这与SynchronizationContext具有相同的问题。 即在调用FromCurrentSyncronizationContext之前,我必须检查当前SynchronizationContext是否为null(对于Console应用程序和本机ui应用程序将是这种情况)。 所以,我再次无法将本机ui应用程序与控制台应用程序区分开来。
当然,我可以让我的库的用户在调用我的Initialize
方法时指定它是否是一个UI应用程序,但我希望尽可能避免该库的用户的复杂性。
这通常是不可能的,易于在线程中使用的库不能对UI线程中的哪个特定线程做出任何假设。 您可以捕获Synchronization.Current,但只有从UI线程调用初始化方法时才能正常工作。 这样做并不是非常不寻常,比如TaskScheduler.FromCurrentSynchronizationContext()往往是偶然的,但不是保证。 你可以添加一个检查,如果Thread.CurrentThread.GetApartmentState()没有返回STA那么你没有从UI线程调用的几率非常高。 在这种情况下,SynchronizationContext.Current也常常为null,另一种检查方式。
(可以说)更好的方法是不要担心它,并让客户端代码弄明白,它不会有任何麻烦编组回调。 或者公开SynchronizationContext类型的属性,以便客户端代码可以分配它。 或者将其添加为构造函数参数。 如果您准备发布但是发现它仍为空,则抛出InvalidOperationException,这是客户端程序员只进行一次的疏忽。
我认为你应该把它作为你的Initialize
方法的一个选项(或以某种方式允许你的调用者请求UI交互),对我来说更有意义。 我不知道具体细节,但在我看来这些是“礼貌”的事情,让你的来电者决定他们是否要你或想要支持你的用户界面。 我会更进一步,甚至作为调用者提供同步上下文。 但那是我的意见。
要回答您的问题,您可以使用一些“黑客”来确定您是否在控制台应用程序中运行。 这个SO问题有一些信息: C#/ .NET:检测程序是作为服务还是作为控制台应用程序运行
将库初始化更改为具有SyncronizationContext参数。 如果参数为null,则库不需要执行任何特殊操作,如果不是null,则发布/发送GUI更新。
我认为这正是AsyncOperationManager.CreateOperation()
的用途。 “实现基于事件的异步模式”指出:
基于事件的异步模式提供了一种打包具有异步function的类的标准化方法。 如果使用AsperOperationManager等辅助类实现,则您的类将在任何应用程序模型下正常工作,包括ASP.NET,控制台应用程序和Windows窗体应用程序。
由调用者决定是否要在UI线程上调用API。 如果他们这样做,这将捕获上下文,事件将按顺序通过消息泵。 在控制台应用程序中,如果您安装了SynchronizationContext,则可以获得相同的行为,这样您可以使用Nito.AsyncEx
nuget包中的AsyncContext.Run()
免费获得。 无需额外的财产或必须自己编写条件代码。 如果没有可用的序列化同步上下文, AsyncOperation.Post()
将使用Console应用程序可用的伪同步上下文,它只是将事件排队到线程池(意味着post可能不按顺序执行)。 只需记住在完成后调用AsyncOperation.OperationCompleted()
或AsyncOperation.PostOperationCompleted()
。
在库中我想在初始化期间捕获一些东西(A SynchronizationContext,Dispatcher,Task Scheduler或其他东西)
这正是AsyncOperationManager.CreateOperation()
所做的,并且与环境无关。 但是你应该尝试将它与对OperationCompleted()
的调用配对,考虑到你想暴露的API,这可能会更难。 使用AsyncOperation
的最简单方法是在库实际启动操作而不是在初始化期间启动操作。 或者通过让初始化例程返回一个IDisposable
上下文对象句柄,该句柄将向消费者发出信号,表明他们需要管理其生命周期。
- 代表们的目标c
- 如何使用ASP.Net Web Api 2进行部分响应
- HttpWebRequest:如何通过带有x-www-form-enclosed的WebRequest在Canada Post找到邮政编码?
- 如何使用csc(C#编译器)或dmcs(单声道C#编译器)生成IL源代码?
- HTTP错误404.13 – asp.net核心2.0
- entity framework首先使用代码进行条件映射?
- 有没有一种简单的方法来validation数据库模式是否正是我期望它使用entity framework?
- WPF /multithreading:MVVM中的UI调度程序
- c#解析xml with和撇号抛出exception