traves thread the than subprocesos other llamar from form example ejemplo created controles control como accessed c# winforms multithreading synchronization

the - thread c# windows form



Capturando el hilo principal SynchronizationContext o Dispatcher de una biblioteca (4)

Cambie la inicialización de la biblioteca para tener un parámetro SyncronizationContext. Si el parámetro es nulo, entonces la biblioteca no necesita hacer nada especial, si no es nulo, las actualizaciones de la GUI de envío / envío allí.

Tengo una biblioteca de C # que le gustaría tener la capacidad de enviar / publicar trabajo al hilo de la interfaz de usuario "principal" (si existe). Esta biblioteca puede ser utilizada por:

  • Una aplicación de winforms
  • Una aplicación nativa (con UI)
  • Una aplicación de consola (sin interfaz de usuario)

En la biblioteca me gustaría capturar algo (un SynchronizationContext, un despachador, un programador de tareas u otra cosa) durante la inicialización, que me permitirá (en un momento posterior) enviar / publicar trabajo al hilo principal (si el el hilo principal tiene esa capacidad, es decir, tiene una bomba de mensajes). Por ejemplo, a la biblioteca le gustaría colocar alguna UI de Winforms en el hilo principal si y solo si la aplicación principal tiene la capacidad para llegar al hilo principal.

Cosas que he intentado:

  1. Un SynchronizationContext : capturar esto funciona bien para una aplicación Winforms (se instalará WindowsFormsSynchronizationContext como Current SynchronizationContext. Esto también funciona bien para la aplicación de consola, ya que puedo detectar que Current SynchronizationContext es nulo (y por lo tanto, sé que no No tengo la capacidad de enviar / publicar trabajo al hilo principal). El problema aquí es la aplicación de interfaz de usuario nativa: tiene la capacidad (es decir, tiene una bomba de mensajes), pero el contexto de Sincronización actual es nulo y por lo tanto puedo '' Diferenciarlo del caso de la aplicación de la Consola. Si pudiera diferenciarme, entonces simplemente podría instalar un WindowsFormsSynchronizationContext en el hilo principal, y estoy listo para continuar.
  2. Un despachador : la captura de esto usando la corriente crea un nuevo SynchronizationContext. Por lo tanto, en todas las situaciones, recibiré un despachador. Sin embargo, para una aplicación de consola, el uso de Dispatcher.Invoke desde un hilo de fondo se bloqueará (como se esperaba). Podría usar Dispatcher.FromThread (que no crea un Dispatcher para el hilo si no existe). Pero la aplicación de UI nativa devolverá un Dispatcher nulo utilizando este método, y entonces, de nuevo, estoy atascado al no poder distinguir la aplicación de la interfaz de usuario de la aplicación de la consola.
  3. Un TaskScheduler : podría usar FromCurrentSynchronizationContext . Esto tiene los mismos problemas que SynchronizationContext. Es decir, antes de llamar FromCurrentSyncronizationContext, tendría que comprobar si el Current SynchronizationContext es nulo (lo cual será el caso para la aplicación Console y la aplicación ui nativa). Entonces, nuevamente, no puedo distinguir la aplicación de interfaz de usuario nativa de la aplicación de consola.

Yo, por supuesto, podría hacer que el usuario de mi biblioteca especifique si es o no una aplicación de interfaz de usuario cuando llaman a mi método Initialize , pero esperaba evitar esa complicación para el usuario de la biblioteca, si es posible.


Creo que deberías hacer de esto una opción para tu método Initialize (o de alguna manera permitir que tu interlocutor solicite la interacción UI), para mí eso tiene más sentido. No conozco los detalles, pero me parece que es una decisión "cortés", deje que su interlocutor decida si quiere o si desea respaldar su UI. Daría un paso más e incluso como la persona que llama para proporcionar un contexto de sincronización. Pero esa es mi opinión.

Para responder a su pregunta, existen algunos "hacks" que puede usar para determinar si se está ejecutando en una aplicación de consola. Esta pregunta SO tiene algo de información sobre eso: C # /. NET: Detecta si el programa se está ejecutando como un servicio o una aplicación de consola


Creo que esto es exactamente para lo que es AsyncOperationManager.CreateOperation() . "Implementando el patrón asincrónico basado en eventos" establece:

El patrón asincrónico basado en eventos proporciona una forma estandarizada para empaquetar una clase que tiene características asíncronas. Si se implementa con clases de ayuda como AsyncOperationManager , su clase funcionará correctamente en cualquier modelo de aplicación, incluidas ASP.NET, aplicaciones de consola y aplicaciones de Windows Forms.

Depende de la persona que llama decidir si desean llamar a su API en el hilo de UI o no. Si lo hacen, esto capturará el contexto y los eventos pasarán por la bomba de mensajes en orden. En una aplicación de consola, puede obtener el mismo comportamiento si instala un SynchronizationContext que obtiene de forma gratuita utilizando AsyncContext.Run() del paquete Nito.AsyncEx nuget. No necesita una propiedad adicional ni tiene que escribir el código condicional usted mismo. Si no está disponible el contexto de sincronización de serialización, AsyncOperation.Post() usará el contexto de sincronización falsa disponible para las aplicaciones de la consola que solo ponen en cola el evento en el grupo de hilos en su lugar (lo que significa que las publicaciones pueden no ejecutarse en orden). Solo recuerda llamar a AsyncOperation.OperationCompleted() o AsyncOperation.PostOperationCompleted() cuando AsyncOperation.PostOperationCompleted() terminado.

En la biblioteca me gustaría capturar algo (A SynchronizationContext, Dispatcher, Task Scheduler u otra cosa) durante la inicialización

Esto es exactamente lo que hace AsyncOperationManager.CreateOperation() , y de una manera AsyncOperationManager.CreateOperation() entorno. Pero debería intentar emparejar esto con una llamada a OperationCompleted() que tal vez sería más difícil dada la API que desea exponer. La forma más fácil de utilizar AsyncOperation sería iniciar una operación cuando su biblioteca realmente inicie una operación en lugar de durante la inicialización. O al hacer que la rutina de inicialización devuelva un IDisposable objeto de contexto identificable que IDisposable al consumidor que necesita administrar su duración.


Esto no es posible en general, una biblioteca que se puede usar en hilos no puede hacer suposiciones sobre qué hilo en particular es el hilo de UI. Puede capturar Sincronización. Actual, pero eso solo funcionará correctamente si su método de inicialización es invocado desde el hilo de la interfaz de usuario. Eso no es terriblemente inusual para funcionar bien, como TaskScheduler. FromCurrentSynchronizationContext () tiende a funcionar por accidente, pero no es una garantía. Puede agregar un cheque, si Thread.CurrentThread.GetApartmentState () no devuelve STA, entonces las probabilidades de que no se le llame desde el hilo de la interfaz de usuario son muy altas. SynchronizationContext.Current también suele ser nulo en ese caso, otra forma de comprobarlo.

Las (mejor) mejores formas son simplemente no preocuparse por ello y dejar que el cliente lo resuelva, no tendrá problemas para coordinar la devolución de llamada. O para exponer una propiedad de tipo SynchronizationContext para que el código del cliente pueda asignarla. O agréguelo como un argumento constructor. Lanza una InvalidOperationException si estás listo para publicar, pero descubre que todavía es nula, eso es un descuido que el programador cliente solo hace una vez.