wpf multithreading user-interface dispatcher

WPF Dispatcher.BeginInvoke y UI/Subprocesos de fondo



multithreading user-interface (5)

Creo que necesito algunas aclaraciones con respecto al uso de Dispatcher.Invoke y Dispatcher.BeginInvoke de WPF.

Supongamos que tengo un código de "trabajo" de larga ejecución como el que se invoca al presionar un botón en una aplicación WPF simple:

longWorkTextBox.Text = "Ready For Work!"; Action workAction = delegate { Console.WriteLine("Starting Work Action"); int i = int.MaxValue; while (i > 0) i--; Console.WriteLine("Ending Work Action"); longWorkTextBox.Text = "Work Complete"; }; longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);

Este código está bloqueando mi interfaz de usuario mientras se realiza la acción de trabajo . Esto se debe a que las invocaciones de Dispatcher siempre se ejecutan en el subproceso de la interfaz de usuario, ¿verdad?

Suponiendo esto, ¿cuál es la mejor práctica para configurar mi despachador para ejecutar workAction en un subproceso separado de mi interfaz de usuario? Sé que puedo agregar un BackgroundWorker a mi workAction para evitar que mi UI se bloquee como tal:

longWorkTextBox.Text = "Ready For Work!"; Action workAction = delegate { BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += delegate { Console.WriteLine("Starting Slow Work"); int i = int.MaxValue; while (i > 0) i--; Console.WriteLine("Ending Work Action"); }; worker.RunWorkerCompleted += delegate { longWorkTextBox.Text = "Work Complete"; }; worker.RunWorkerAsync(); }; longWorkTextBox.Dispatcher.BeginInvoke(DispatcherPriority.Background, workAction);

¿Hay alguna forma más elegante de hacer esto además de usar BackgroundWorker ? Siempre he escuchado que BackgroundWorker es peculiar, por lo que tengo curiosidad por conocer algunas alternativas.


A mi tampoco me gusta BackgroundWorker. Una alternativa simple puede ser algo como:

using System; using System.Threading; using System.Windows; namespace Sample { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); longWorkTextBox.Text = "Ready For Work!"; } private void startButton_Click(object sender, RoutedEventArgs e) { new Thread(Work).Start(); } void Work() { longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Working..."; })); Console.WriteLine("Starting Work Action"); int i = int.MaxValue; while (i > 0) i--; Console.WriteLine("Ending Work Action"); longWorkTextBox.Dispatcher.BeginInvoke((Action)(() => { longWorkTextBox.Text = "Work Complete"; })); } } }

Fácil, no?


Como su nombre lo indica, se ejecutará en segundo plano, por lo que no es necesario crear una instancia con Dispatcher. Además, si desea que este código se ejecute en un WP7, BeginInvoke no obtiene el parámetro de fondo.

Mi recomendación es crear el BackgroundWorker como:

BackgroundWorker worker = new BackgroundWorker;

Y luego crear los controladores para los eventos:

worker.WorkerReportsProgress = true; worker.WorkerSupportsCancellation = true; worker.DoWork +=new DoWorkEventHandler(worker_DoWork); worker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); worker.ProgressChanged +=new ProgressChangedEventHandler(worker_ProgressChanged);

Y finalmente llamas:

bkwkPlayingLoop.RunWorkerAsync();

Es una gran tentación utilizar el Dispatcher desde el interior de DoWork, pero llamar a worker.ReportProgress () y manejar la IU desde allí. De lo contrario, enfrentará algunas inconsistencias con la activación de eventos de terminación.


La respuesta de Charlie es lo que estás buscando, de verdad.

Sin embargo, si es posible, puede ver si puede o no repartir su trabajo para que las unidades de trabajo individuales sean pequeñas y no afecten tanto a la IU. Esto le permitiría usar el Dispatcher directamente. Hay un buen ejemplo de esto en la página de subprocesos de WPF: https://msdn.microsoft.com/en-us/library/ms741870%28v=vs.100%29.aspx


Las tareas son más fáciles de usar que los empleados de segundo plano, hacen más cosas, tienen menos problemas y se crearon en gran medida, por lo que ya no es necesario utilizar más a los trabajadores de fondo ...


Sinceramente, creo que BackgroundWorker es la solución más elegante para esto. No puedo pensar en una forma más sencilla de hacerlo.