Capturing the main thread SynchronizationContext or Dispatcher from a library

谁都会走 提交于 2019-12-03 12:25:21

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.

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.

CodingGorilla

I think you should make this an option to your Initialize method (or somehow allow your caller to request UI interaction), to me that just makes more sense. I don't know the specifics but these seems to me to be a "courteous" thing to do, let your caller decide if they want you to or want to support your UI. I would take it one step further and even as the caller to supply a synchronization context. But that's my opinion.

To answer your question, there are a few "hacks" you can use to determine if you're running in a console applicaiton. This SO question has some information on that: C#/.NET: Detect whether program is being run as a service or a console application

Change the library initialization to have a SyncronizationContext parameter. If the parameter is null then the library doesn't need to do anything special, if not null Post/Send GUI updates there.

I think this is exactly what AsyncOperationManager.CreateOperation() is for.“Implementing the Event-based Asynchronous Pattern” states:

The Event-based Asynchronous Pattern provides a standardized way to package a class that has asynchronous features. If implemented with helper classes like AsyncOperationManager, your class will work correctly under any application model, including ASP.NET, Console applications, and Windows Forms applications.

It is up to the caller to decide if they want to call your API on the UI thread or not. If they do, this will capture the context and events will go through the message pump in order. In a Console application you can get the same behavior if you install a SynchronizationContext such you get for free by using AsyncContext.Run() from the Nito.AsyncEx nuget package. No need for an extra property or to have to write the conditional code yourself. If no serializing synchronization context is available, AsyncOperation.Post() will use the fake synchronization context available to Console apps which just queue the event to the threadpool instead (meaning that the posts may not execute in order). Just remember to call AsyncOperation.OperationCompleted() or AsyncOperation.PostOperationCompleted() when done.

In the library I'd like to capture something (A SynchronizationContext, a Dispatcher, a Task Scheduler, or something else) during initialization

This is exactly what AsyncOperationManager.CreateOperation() does, and in an environment-agnostic way. But you should try to pair this with a call to OperationCompleted() which maybe would be more difficult given the API you want to expose. The easiest way to use AsyncOperation would be to start an operation when your library actually starts an operation instead of during initialization. Or by having the initialization routine return an IDisposable context object handle which would signal to the consumer that they need to manage its lifetime.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!