subproceso que net hilos false ejemplo distinto desde delegados creó control checkforillegalcrossthreadcalls aquel c# wpf multithreading observablecollection

c# - que - invoke en vb net



¿Cómo actualizo un ObservableCollection a través de un hilo de trabajo? (3)

Tengo un ObservableCollection<A> a_collection; La colección contiene ''n'' elementos. Cada elemento A se ve así:

public class A : INotifyPropertyChanged { public ObservableCollection<B> b_subcollection; Thread m_worker; }

Básicamente, está conectado a una vista de lista de WPF + a un control de vista de detalles que muestra la b_subcollection del elemento seleccionado en una vista de lista separada (enlaces bidireccionales, actualizaciones en cambios de propiedad, etc.). El problema apareció cuando comencé a implementar el enhebrado. Toda la idea era hacer que toda la colección a utilizara su hilo de trabajo para "hacer el trabajo" y luego actualizar sus respectivas b_subcollections y hacer que la GUI muestre los resultados en tiempo real.

Cuando lo probé, recibí una excepción que decía que solo el hilo Dispatcher puede modificar un ObservableCollection, y el trabajo se detuvo.

¿Alguien puede explicar el problema y cómo evitarlo?

Aclamaciones


Nueva opción para .NET 4.5

A partir de .NET 4.5, hay un mecanismo incorporado para sincronizar automáticamente el acceso a la recopilación y despachar eventos CollectionChanged al subproceso de la interfaz de usuario. Para habilitar esta función, debe llamar a BindingOperations.EnableCollectionSynchronization desde su subproceso de interfaz de usuario .

EnableCollectionSynchronization hace dos cosas:

  1. Recuerda el hilo desde el que se llama y hace que la canalización de enlace de datos coordine eventos CollectionChanged en ese hilo.
  2. Adquiere un bloqueo en la colección hasta que se haya manejado el evento marshalled, de modo que los controladores de eventos que ejecutan el subproceso de la interfaz de usuario no intenten leer la colección mientras se está modificando a partir de un hilo de fondo.

Muy importante, esto no se ocupa de todo : para asegurar el acceso seguro a subprocesos a una colección intrínsecamente no segura para subprocesos, debe cooperar con el marco adquiriendo el mismo bloqueo de sus subprocesos de fondo cuando la colección está a punto de modificarse.

Por lo tanto, los pasos necesarios para una operación correcta son:

1. Decida qué tipo de bloqueo usará

Esto determinará qué sobrecarga de EnableCollectionSynchronization debe usarse. La mayoría de las veces bastará con una instrucción de lock simple, por lo que esta sobrecarga es la opción estándar, pero si está utilizando un mecanismo de sincronización sofisticado, también hay soporte para bloqueos personalizados .

2. Crea la colección y habilita la sincronización

Dependiendo del mecanismo de bloqueo elegido, llame a la sobrecarga apropiada en el hilo de UI . Si utiliza una instrucción de lock estándar, debe proporcionar el objeto de bloqueo como argumento. Si utiliza la sincronización personalizada, debe proporcionar un delegado CollectionSynchronizationCallback y un objeto de contexto (que puede ser null ). Cuando se invoca, este delegado debe adquirir su bloqueo personalizado, invocar la Action pasó y liberar el bloqueo antes de volver.

3. Coopere bloqueando la colección antes de modificarla

También debe bloquear la colección con el mismo mecanismo cuando vaya a modificarla usted mismo; Haga esto con lock() en el mismo objeto de bloqueo pasado a EnableCollectionSynchronization en el escenario simple, o con el mismo mecanismo de sincronización personalizado en el escenario personalizado.


Con .NET 4.0 puedes usar estos one-liners:

.Add

Application.Current.Dispatcher.BeginInvoke(new Action(() => this.MyObservableCollection.Add(myItem)));

.Remove

Application.Current.Dispatcher.BeginInvoke(new Func<bool>(() => this.MyObservableCollection.Remove(myItem)));


Técnicamente, el problema no es que estés actualizando el ObservableCollection desde un hilo de fondo. El problema es que cuando lo haces, la colección aumenta su evento CollectionChanged en el mismo hilo que causó el cambio, lo que significa que los controles se actualizan desde un hilo de fondo.

Para poblar una colección de un hilo de fondo mientras los controles están ligados a él, probablemente tenga que crear su propio tipo de colección desde cero para poder solucionar esto. Sin embargo, hay una opción más simple que puede funcionar.

Publique Agregar llamadas en el hilo de la interfaz de usuario.

public static void AddOnUI<T>(this ICollection<T> collection, T item) { Action<T> addMethod = collection.Add; Application.Current.Dispatcher.BeginInvoke( addMethod, item ); } ... b_subcollection.AddOnUI(new B());

Este método volverá inmediatamente (antes de que el elemento se agregue realmente a la colección) y luego en el hilo de la interfaz de usuario, el elemento se agregará a la colección y todos estarán contentos.

La realidad, sin embargo, es que esta solución probablemente se empantane bajo mucha carga debido a toda la actividad de hilos cruzados. Una solución más eficiente sería agrupar varios elementos y publicarlos en el hilo de la interfaz de usuario periódicamente para que no esté llamando a través de subprocesos para cada elemento.

La clase BackgroundWorker implementa un patrón que le permite informar el progreso a través de su método ReportProgress durante una operación en segundo plano. El progreso se informa en el subproceso de interfaz de usuario a través del evento ProgressChanged. Esta puede ser otra opción para ti.