2011-03-28 9 views
6

El uso de .NET 3.5Aplicación WPF de subprocesos múltiples: invocación de despachador. Una forma más eficiente?

Hola chicos, estoy haciendo una aplicación de WPF para un proyecto y que solo estaba buscando un poco de conocimiento en relación con el Dispatcher y multihilo. Un ejemplo de mi programa:

Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
         () =>_aCollection.Add(new Model(aList[i], aSize[i])))); 

Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
         () => _Data.Add(new DataPoint<double, double>(Id, aList[i])))); 

Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
         () => _historical[0].Add(aList[i]))); 

entiendo que WPF no le gusta cuando otro hilo acceso a un objeto distinto del que lo creó. Sin embargo, estaba pensando que seguramente había una mejor manera de hacer tantas invocaciones de despachadores, ¿podría alguien por favor empujarme en la dirección correcta al menos? (Si es que hay una solución mejor).

Saludos, Sparky

Respuesta

14

Puede empezar por ser menos detallado en sus llamadas, es decir

Application.Current.Dispatcher.Invoke(() =>_aCollection.Add(new Model(aList[i], aSize[i]))); 

Otro truco que me gusta usar es para hacer un método de acceso directo como esto:

public static void UiInvoke(Action a) 
{ 
    Application.Current.Dispatcher.Invoke(a); 
} 

Entonces usted tiene incluso menos que hacer, como en:

UiInvoke(() =>_aCollection.Add(new Model(aList[i], aSize[i]))); 

Usar dispatcher.Invoke() es realmente solo cómo recuperar la acción en el hilo de la interfaz de usuario, que es probablemente donde se crearon estos objetos (_aCollection) en primer lugar. Si los elementos en cuestión no tienen interacción directa con el hilo de la interfaz de usuario, entonces puede crearlos/manipularlos en un hilo diferente, eliminando la necesidad de utilizar el despachador. Por supuesto, este enfoque podría volverse más complicado dependiendo de lo que esté haciendo.

+0

¡Me gusta esta idea! Gracias. Tengo 2 hilos ejecutándose en una clase: 1 para obtener datos para la base de datos, y el otro para emitir un RaisePropertyChanged para actualizar la UI. Entiendo que probablemente esta no sea la mejor manera de hacer las cosas, para ser honesto, no soy un experto en WPF, por lo que la idea de un UI Thread y backgroundWorkers es un poco extraño para mí en el minuto – Sparky

+1

No, eso es más o menos cómo se hacen las cosas en WPF, así que diría que su enfoque está bien. Estoy de acuerdo en que puede ser un dolor hacer las cosas de UI, pero creo que es un mal necesario. Codificar al gorila también es un buen punto sobre las continuidades, pero pueden entrar en territorio de súper escurrimiento, así que ten cuidado. ¡Buena suerte! –

+0

No creo que esto sea "más o menos cómo se hacen las cosas en WPF". La unión de datos debería hacer el envío de forma automática. Especialmente si él está usando MVVM. Ver mi respuesta actualizada. – Euphoric

12

La forma más fácil sería combinar las tres llamadas en una sola:

Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(
         () => 
         { 
          _aCollection.Add(new Model(aList[i], aSize[i]); 
          _Data.Add(new DataPoint<double, double>(Id, aList[i]); 
          _historical[0].Add(aList[i]) 
         })); 
+0

Yo no sabía que era posible, aprender algo nuevo cada día! Aclamaciones. – Sparky

+1

@Sparky: Todo lo que está haciendo con el operador '=>' es crear una expresión lambda, que es un tipo de delegado anónimo.Todos estos son azúcares sintácticos que el compilador termina convirtiéndose en métodos de instancias ordinarias (o a veces estáticas). Puedes hacer cualquier cosa dentro de una expresión lambda (cuando se usa como delegado anónimo, como está aquí) que puedes hacer dentro de una función ordinaria, excepto por (posiblemente, no he marcado) los parámetros 'ref' y' out' . –

+0

Me funcionó, eliminé la excepción de impresión: el hilo de llamada no puede acceder a este objeto porque lo posee un hilo diferente. Stacktrace: en System.Windows.Threading.Dispatcher.VerifyAccess() – digz6666

6

si estás usando .Net 4.0 Me gustaría ver en el uso de System.Threading.Tasks. Esto parece ser un buen ejemplo para continuations.

+1

Las tareas no ayudarán si ya está fuera del hilo de la bomba de mensajes (que es donde la sincronización automática sería útil) –

+5

Claro que sí, porque puedes programar tus tareas de "Fondo" en un hilo de fondo y luego "Terminar" con una continuación en el hilo de UI como : 'Task.ContinueWith (FinishMyStuff, TaskScheduler.FromCurrentSynchronizationContext)' – CodingGorilla

+1

Claro, si el OP está en posición de mover todo el código actual en Tareas y colocarlo todo en la Ventana/Control. Sin embargo, si no está en posición de volver a trabajar todo el modelo de subprocesamiento, entonces Tasks no ayudará. –

2

Su problema proviene del hecho de que ObservableCollection no envía cambios automáticamente al hilo de la interfaz de usuario. Esto es diferente de INotifyPropertyChanged simple, que lo hace de forma automática. Recomiendo crear tu propia ObservableCollection específica, que implemente INotifyCollectionChanged, que automaticamente distribuya los cambios al hilo de UI.

Se puede ver ejemplo aquí: SynchronizedObservableCollection and BindableCollection

viejo respuesta/pregunta: ¿Está utilizando DependencyObject y DependencyProperties para su unión? Si es así, entonces suéltalo. Se discutió muchas veces y esta es una de las razones más importantes por las que se debe usar INotifyPropertyChanged. La única necesidad de usar dispatcher es modificar las propiedades de los objetos de la GUI y es obvio por su ejemplo, eso no es lo que está haciendo. Y el enlace se ejecuta automáticamente a través del despachador.

Véase también View Models: POCOs versus DependencyObjects

+0

Hola, Eufórico, aplausos, pero no, no estoy usando Dependency Properties. Además, estoy usando MVVM light así que actualmente uso "RaisePropertyChanged" sobre "INotifyPropertyChanged". Gracias – Sparky

+0

Eso es raro. Entonces, ¿qué son _aCollection, _Data e _historical en tu ejemplo? Si no son parte de GUI, entonces no hay necesidad de cambiarlos a través de Dispatcher. Y no veo CUALQUIER necesidad de usar Dispatcher cuando se usa MVVM. – Euphoric

+0

Hmmm ... Bueno, en ese caso, ¿cómo resolvería el problema donde los hilos sin nombre acceden a una Colección Observable? _Data son puntos de gráfico para Visiblox y _aCollection es una colección observable enlazada por una lista – Sparky

Cuestiones relacionadas