2010-02-19 25 views
26

Tengo un ObservableCollection en mi clase. Y más adentro de mi clase tengo un hilo. De este hilo me gustaría agregar a mi ObservableCollection. Pero no puedo hacer esto:ObservableCollection and threading

Este tipo de CollectionView no admite cambios en su SourceCollection de un hilo diferente al hilo despachador.

Tenga en cuenta que esto no está sucediendo desde el subproceso de interfaz de usuario, por lo que no tienen acceso al despachador.

Respuesta

16

El enfoque de JaredPar es válido. Otro enfoque que vale la pena considerar es usar un hilo seguro ObservableCollection en lugar del ObservableCollection incorporado. Hay algunas implementaciones, pero Sasha Barber's implementation y CLinq Continuous Collection class son algunas de las mejores en mi opinión. Internamente, estas clases usan esencialmente el enfoque descrito por JaredPar, pero lo encapsulan dentro de la clase de colección.

+0

Personalmente, no pude hacer que funcione con los enlaces proporcionados. Necesitaba utilizar el enfoque de JaredPas que mencionaste. –

+0

Pruebe el siguiente enlace que proporciona una solución de seguridad de subprocesos que funciona desde cualquier subproceso y se puede vincular a través de varios subprocesos de UI: http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection – Anthony

+0

El artículo que hace referencia por AnthonyPaulO requiere el paquete Inmutable Collections NuGet y solo funciona con .NET4.5. ¿Hay algo similar para .NET4.0? – Dave

13

La mejor manera de resolver esto es pasar el objeto Dispatcher al método de inicio del subproceso en segundo plano.

void DoBackgroundOperation(ObservableCollection<SomeType> col) { 
    var dispatcher = Dispatcher.CurrentDispatcher; 
    ThreadStart start =() => BackgroundStart(dispatcher, col); 
    var t = new Thread(start); 
    t.Start(); 
} 

private static void BackgroundStart(
    Dispatcher dispatcher, 
    ObservableCollection<SomeType> col) { 
    ... 
    SomeType t = GetSomeTypeObject(); 
    Action del =() => col.Add(t); 
    dispatcher.Invoke(del); 
} 

Ahora más adelante, cuando es necesario agregar a la colección se puede utilizar el objeto de interfaz de usuario Dispatcher.

Como señaló @Reed, se logra una solución más general al usar SynchronizationContext. Aquí hay una muestra de estilo funcional que usa SynchronizationContext para crear un delegado responsable de agregar nuevos valores. Esto tiene la ventaja de ocultar tanto la colección como el modelo de subprocesamiento desde el código que crea el objeto.

void DoBackgroundOperation(ObservableCollection<SomeType> col) { 
    var context = SynchronizationContext.Current; 
    Action<SomeType> addFunc = (SomeType st) => context.Send(() => col.Add(st), null); 
    ThreadStart start =() => BackgroundStart(addFunc); 
    var t = new Thread(start); 
    t.Start(); 
} 

private static void BackgroundStart(Action<SomeType> addFunc) { 
    ... 
    SomeType t = GetSomeTypeObject(); 
    addFunc(t); 
} 
+3

1: También, el mismo enfoque se puede hacer usando SynchronizationContext.Current en lugar del dispatcher, si no desea tomar una dependencia de WPF (es decir: también quiere usar este código en Windows Forms). –

+0

@Reed, agregó una solución con 'SynchronizationContext' que tiene un sabor funcional. – JaredPar

+0

Gracias por su respuesta. Voy a ver esto mañana ya que es 01:30 a.m. aquí. – ErikTJ