Domanda

Supponiamo che ho questo pezzo di codice.

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

Voglio spostare string result = TimeConsumingTask(value); ad alcuni thread in background. Qual è il modo più semplice ma efficace di spostare questo thread in background, se

- Order does matter
- Order doesn't matter
È stato utile?

Soluzione

Un modo semplice per fine garanzia è di fare tutte le TimeConsumingTasks sequenzialmente nello stesso filo di fondo.

Questa operazione potrebbe richiedere un po 'più lungo di esecuzione più TimeConsumingTasks su più thread, ma salva un sacco di mal di testa cercando di sincronizzare i risultati e di uscita dei prodotti nell'ordine richiesto.

Se il tuo obiettivo principale è quello di liberare l'interfaccia utente in modo che l'utente può continuare a lavorare su qualcosa d'altro, la differenza di tempo di completamento tra l'utilizzo di un thread e discussioni più probabilmente è trascurabile, in modo da andare via più semplice - utilizzare un unico thread in background per tutti i TimeConsumingTasks.

Se il tuo obiettivo principale è quello di rendere il calcolo delle TimeConsumingTasks completi in modo più rapido, poi eseguire più thread è probabilmente l'approccio migliore in quanto è più probabile per sfruttare più core ed eseguire alcuni dei lavori in parallelo.

Per mantenere l'ordine nel caso più thread, si dovrà ordinare i risultati da soli. Si sia possibile mantenere un elenco accumulatore di temperatura ed inserire ogni risultato in esso in posizione appropriata del risultato nella lista (se la posizione / indice è facilmente calcolato o conosciuto), oppure aggiungere un fase di lavorazione aggiuntiva dopo che tutti i fili hanno completato a fondersi le molteplici risultati nell'output finale nell'ordine desiderato. Parallelamente e circoli calcolo distribuito, il processo di scissione un problema in più pezzi da completare in parallelo e quindi combinare i risultati in un ordine coerente si chiama "riduzione map".

Altri suggerimenti

In un'applicazione GUI, come WPF, WinForms, Silverlight, ... si potrebbe usare un BackgroundWorker come si prende cura di marshalling diverse callback che si verificano sul thread principale che è importante quando l'aggiornamento dell'interfaccia utente. In ASP.NET è possibile dare un'occhiata a pagine asincrone .

Facciamo un esempio, se l'ordine non importa con un lavoratore di 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();

Ci sono molti modi per la pelle questo gatto. Negli ultimi anni ho usato molti, BackgroundWorker essendo il più comune e semplice. Tuttavia da ora in poi, hai avuto modo di pensare il Async CTP è il modo andare. Almeno rivedere l'intro prima di decidere. In termini di semplicità e, codice conciso pulita è una parte del codice più caldo per mai essere scritto. :)

Vorrei usare una task. In questo modo l'intera operazione viene eseguito in background senza bloccare l'interfaccia utente. Essere sicuri, però, per aggiornare la casella di riepilogo sul thread principale. Non sono sicuro di come fare in WinForms, ma in WPF a realizzare utilizzando Dispatcher.Invoke() o Dispatcher.BeginInvoke() come qui di seguito.

Se le questioni di ordinamento:

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

Se l'ordinazione non importa si potrebbe anche usare Parallel.ForEach().

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

Si deve attendere il risultato prima di poter aggiornare l'interfaccia utente, quindi non c'è alcun senso nel muovere solo la TimeConsumingTask () per il thread in background. Sarà necessario aggiornare l'interfaccia utente dal thread in background (ma di smistamento la chiamata al thread UI).

Dipende dal vostro obiettivo finale, ma una semplice soluzione è solo per dare il via un compito. Ciò funzionerà se l'ordine non importa.

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

Utilizzando BackgroundWorker è anche abbastanza facile, ma non è così "semplice", dal momento che non v'è più il codice:

  • creare un BackgroundWorker
  • Assegna un DoWork handler
  • assegnare al gestore Progress
  • set WorkerReportsProgress
  • chiamata RunWorkerAsync ()

Se l'ordine non importa, è possibile utilizzare il ciclo Parallel.ForEach. ( A cura secondo le dthorpe e commenti di 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);

Se si vuole richiamata l'interfaccia utente con le informazioni di processo, mi consiglia di utilizzare un BackgroundWorker come Darin Dimitrov raccomanda nella sua risposta.

C'è una grande differenza tra il multithreading e l'elaborazione in background, anche se è possibile utilizzarli entrambi allo stesso tempo.

sfondo di elaborazione - utilizzando BackgroundWorker come suggerisce Darin - è possibile consentire all'utente di continuare a lavorare nell'interfaccia utente senza attendere che il processo in background per completo. Se l'utente deve attendere per il trattamento alla fine prima di poter procedere, poi c'è nessun punto in esecuzione in background.

multithreading - utilizzando Parallel Extensions libreria , forse - è possibile utilizzare più thread per eseguire il ciclo ripetitivo in parallelo in modo che completa più veloce. Questo sarebbe utile se l'utente è in attesa del processo prima di poter continuare.

Infine, se si esegue qualcosa in background, è possibile ottenere che finisca velocemente utilizzando il multithreading.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top