Pregunta

Supongamos que tengo este código.

foreach(string value in myList)
  {
      string result = TimeConsumingTask(value);
      //update UI
      listBox.Add(result);
  }

quiero moverme string result = TimeConsumingTask(value); a algún hilo de fondo. ¿Cuál es la forma más simple pero eficiente de mover esto al hilo de fondo si

- Order does matter
- Order doesn't matter
¿Fue útil?

Solución

Una forma simple de garantizar el orden es hacer todas las tareas de tiempo de tiempo secuencialmente en el mismo hilo de fondo.

Esto puede llevar un poco más tiempo que ejecutar múltiples tareas de tiempo de tiempo en múltiples hilos, pero le ahorra mucho dolor de cabeza tratando de sincronizar los resultados y producir la salida en el orden requerido.

Si su objetivo principal es liberar la interfaz de usuario para que el usuario pueda continuar trabajando en algo más, la diferencia en el tiempo de finalización entre usar un hilo y múltiples hilos es probablemente insignificante, así que vaya a la ruta más simple: use un solo hilo de fondo para todos los hilos de fondo para todos los TimeconsumingTasks.

Si su objetivo principal es hacer que el cálculo de las tareas de tiempo de tiempo se complete más rápidamente, entonces ejecutar múltiples hilos es probablemente el mejor enfoque, ya que es más probable que aproveche múltiples núcleos y ejecute parte del trabajo en paralelo.

Para preservar el orden en el caso de múltiples hilos, deberá ordenar los resultados usted mismo. Puede mantener una lista de tempulator de acumulador e insertar cada resultado en la ubicación apropiada del resultado en la lista (si la ubicación / índice se calcula o conoce fácilmente), o agrega un paso de procesamiento adicional después de que todos los subprocesos se hayan completado para fusionar Los múltiples resultados en la salida final en el orden deseado. En los círculos informáticos paralelos y distribuidos, el proceso de dividir un problema en múltiples fragmentos para completarse en paralelo y luego combinar los resultados en un orden coherente se llama "reducción del mapa".

Otros consejos

En una aplicación GUI como WPF, WinForms, Silverlight, ... podría usar un Trabajador de fondo ya que se encarga de organizar las diferentes devoluciones de llamada que ocurren en el hilo principal, lo cual es importante al actualizar la interfaz de usuario. En Asp.net puede echar un vistazo a páginas asíncronas.

Tomemos un ejemplo si el pedido es importante con un trabajador de fondo:

var worker = new BackgroundWorker();
worker.DoWork += (obj, args) =>
{
    args.Result = myList.Select(TimeConsumingTask).ToList();
};
worker.RunWorkerCompleted += (obj, args) =>
{
    var result = (IList<string>)args.Result;
    foreach(string value in result)
    {
        listBox.Add(result);
    }
}; 
worker.RunWorkerAsync();

Hay muchas maneras de lucir este gato. Los últimos años he usado muchos, BackgroundWorker siendo el más común y directo. Sin embargo, a partir de ahora, tienes que pensar el Asíncrono CTP es el camino a seguir. Al menos Revisar la introducción antes de decidir. En términos de simplicidad y código limpio y conciso, es algunos de los códigos más populares que se hayan escrito. :)

Usaría una tarea. De esa manera, toda la operación se ejecuta en segundo plano sin bloquear la interfaz de usuario. Sin embargo, asegúrese de actualizar el cuadro de lista en el hilo principal. No estoy seguro de cómo hacer eso en Winforms, pero en WPF lo logras usando Dispatcher.Invoke() o Dispatcher.BeginInvoke() como abajo.

Si ordenar es importante:

Task.Factory.StartNew(
    () =>
        {
            foreach (string value in myList)
            {
                string result = TimeConsumingTask(value);
                //update UI
                Application.Current.Dispatcher.BeginInvoke(
                    (Action)(() => listBox.Add(result)));
            }            
        });

Si el pedido no importa que también pueda usar Parallel.ForEach().

Task.Factory.StartNew(
    () =>
        {
            Parallel.ForEach(myList, 
                value =>
                {
                    string result = TimeConsumingTask(value);
                    //update UI
                    Application.Current.Dispatcher.BeginInvoke(
                       (Action)(() => listBox.Add(result)));
                });            
        });
    }

Debe esperar el resultado antes de poder actualizar la interfaz de usuario, por lo que no tiene sentido mover solo TimeConsumingTask () al hilo de fondo. Deberá actualizar la interfaz de usuario desde el hilo de fondo (pero organizar la llamada al hilo de la interfaz de usuario).

Depende de su objetivo final, pero una solución simple es simplemente iniciar una tarea. Esto funcionará si el pedido importa.

Task.Factory.StartNew(
    () => 
    {
        foreach(string value in myList)   
        {       
            string result = TimeConsumingTask(value);       
            listBox.Invoke(new Action(() => listBox.Add(result)));   
        } 
    });

Usar Fondo Worker también es bastante fácil, pero no como "simple", ya que hay más código:

  • crear un trabajador de fondo
  • Asignar un manejador Dowork
  • Asignar un manejador de progreso
  • Establecer WorkerReportsProgress
  • Llame a RunworkeraSync ()

Si el pedido no importa, puede usar el Parallel.ForEach círculo. (Editado según los comentarios de Dthorpe y Ttymatty).

using System.Threading.Tasks;

var results = new List<string>();

Parallel.ForEach(myList, value =>
{
    string result = TimeConsumingTask(value);
    results.Add(result);
});

//update UI
listBox.AddRange(results);

Si desea devolver la llamada de la interfaz de usuario con información del proceso, recomendaría usar un Trabajador de fondo Como Darin Dimitrov recomienda en su respuesta.

Hay una gran diferencia entre el procesamiento de múltiples lectura y fondo, aunque puede usarlos al mismo tiempo.

Con procesamiento de antecedentes - Uso de Backgroundworker como sugiere Darin- puede permitir que el usuario continúe trabajando en la interfaz de usuario sin esperar a que se complete el proceso de fondo. Si el usuario tiene que esperar a que finalice el procesamiento antes de que pueda continuar, entonces no tiene sentido ejecutarlo en segundo plano.

Con múltiples lectura -- usando Biblioteca de extensiones paralelas, tal vez, puede usar múltiples hilos para ejecutar el bucle repetitivo en paralelo para que se complete más rápido. Esto sería beneficioso si su usuario está esperando que el proceso finalice antes de que pueda continuar.

Finalmente, si está ejecutando algo en segundo plano, puede hacer que termine más rápido usando múltiples lecturas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top