Frage

Nehmen wir an, ich habe dieses Stück Code.

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

ich möchte umziehen string result = TimeConsumingTask(value); zu einem Hintergrund -Thread. Was ist der einfachste und dennoch effiziente Weg, um dies in Hintergrund -Thread zu verschieben, wenn

- Order does matter
- Order doesn't matter
War es hilfreich?

Lösung

Eine einfache Möglichkeit, die Reihenfolge zu garantieren, besteht darin, alle Zeitungen nacheinander im selben Hintergrund -Thread durchzuführen.

Dies kann etwas länger dauern als die Ausführung mehrerer Zeitconsumingtasks auf mehreren Threads, spart Ihnen jedoch viel Kopfschmerzen, um die Ergebnisse zu synchronisieren und die Ausgabe in der erforderlichen Reihenfolge zu erzeugen.

Wenn Ihr Hauptziel darin besteht, die Benutzeroberfläche zu freien Zeitconsumingtasks.

Wenn Ihr Hauptziel darin besteht, die Berechnung der Zeit- und Ausführungszeiten schneller zu machen, ist das Ausführen mehrerer Threads wahrscheinlich der bessere Ansatz, da es eher mehrere Kerne nutzt und einen Teil der parallelen Arbeiten ausführt.

Um die Reihenfolge im Fall mehrerer Threads zu erhalten, müssen Sie die Ergebnisse selbst bestellen. Sie können entweder eine Akkumulator -Temperaturliste beibehalten und jedes Ergebnis am entsprechenden Speicherort des Ergebniss in der Liste einfügen (falls der Speicherort / Index leicht berechnet oder bekannt ist), oder Sie fügen einen zusätzlichen Verarbeitungsschritt hinzu, nachdem alle Threads zum Koalesce abgeschlossen sind Die mehrfachen Ergebnisse in die endgültige Ausgabe in der gewünschten Reihenfolge. Parallele und verteilte Computerkreise wird der Prozess der Aufteilung eines Problems in mehrere Stücke, die parallel abgeschlossen werden sollen, und die Kombination der Ergebnisse in eine kohärente Reihenfolge als "Kartenreduktion" zu kombinieren.

Andere Tipps

In einer GUI -Anwendung wie WPF, Winforms, Silverlight, ... können Sie a verwenden Hintergrundbearbeiter Da es sich um das Marschieren der verschiedenen Rückrufe auf dem Hauptfaden kümmert, ist dies bei der Aktualisierung der Benutzeroberfläche wichtig. In ASP.NET können Sie sich ansehen Asynchrone Seiten.

Nehmen wir ein Beispiel, wenn die Bestellung mit einem Hintergrundarbeiter von Bedeutung ist:

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();

Es gibt viele Möglichkeiten, diese Katze zu häuten. In den letzten Jahren habe ich viele benutzt, BackgroundWorker die häufigsten und unkompliziertesten sein. Von nun an muss man jedoch denken Async CTP ist der richtige Weg. Wenigstens Überprüfen Sie das Intro Vor der Entscheidung. In Bezug auf Einfachheit und sauberes, prägnanter Code ist es ein Teil des heißesten Code, der jemals geschrieben wurde. :)

Ich würde eine Aufgabe verwenden. Auf diese Weise läuft die gesamte Operation im Hintergrund, ohne die Benutzeroberfläche zu blockieren. Stellen Sie jedoch sicher, dass das Listenfeld im Haupt -Thread aktualisiert wird. Ich bin mir nicht sicher, wie ich das in Winforms machen soll, aber in WPF erreichen Sie es mithilfe Dispatcher.Invoke() oder Dispatcher.BeginInvoke() Wie unten.

Wenn Sie wichtig sind:

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

Wenn die Bestellung keine Rolle spielt, könnten Sie auch verwenden Parallel.ForEach().

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

Sie müssen auf das Ergebnis warten, bevor Sie die Benutzeroberfläche aktualisieren können. Es gibt also keinen Sinn, nur den Zeitpunkt in den Hintergrund -Thread zu verschieben. Sie müssen die Benutzeroberfläche aus dem Hintergrund -Thread aktualisieren (aber den Anruf zum UI -Thread marschallt).

Es hängt von Ihrem ultimativen Ziel ab, aber eine einfache Lösung besteht darin, eine Aufgabe zu eröffnen. Dies wird funktionieren, wenn die Bestellung wichtig ist.

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

Die Verwendung von Hintergrundarbeitern ist auch ziemlich einfach, aber nicht so "einfach", da es mehr Code gibt:

  • Erstellen Sie einen Hintergrundarbeiter
  • Weisen Sie einen Dowork -Handler zu
  • Weisen Sie einen Fortschrittshandler zu
  • Set WorkerportSProgress
  • RunworkerAsync () anrufen

Wenn die Bestellung keine Rolle spielt, können Sie die verwenden Parallel.ForEach Schleife. (Bearbeitet nach den Kommentaren von Dthorpe und Ttttymatty).

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);

Wenn Sie die Benutzeroberfläche mit Prozessinformationen zurückrufen möchten, würde ich empfehlen, eine zu verwenden Hintergrundbearbeiter Wie Darin Dimitrov in seiner Antwort empfiehlt.

Es gibt einen großen Unterschied zwischen Multithreading und Hintergrundverarbeitung, obwohl Sie sie beide gleichzeitig verwenden können.

Mit Hintergrundverarbeitung - Verwenden Sie den Hintergrundworker, wie Darin vorschlägt- Sie können dem Benutzer erlauben, weiterhin in der Benutzeroberfläche zu arbeiten, ohne auf den Hintergrundvorgang zu warten. Wenn der Benutzer warten muss, bis die Verarbeitung abgeschlossen ist, bevor er fortfahren kann, macht es keinen Sinn, sie im Hintergrund auszuführen.

Mit Multithreading - Verwendung Parallele Extensions -Bibliothek, Vielleicht - Sie können mehrere Threads verwenden, um die sich wiederholende Schleife parallel auszuführen, damit sie schneller abgeschlossen wird. Dies wäre von Vorteil, wenn Ihr Benutzer darauf wartet, dass der Prozess fertig ist, bevor er fortgesetzt werden kann.

Wenn Sie etwas im Hintergrund ausführen, können Sie es mit Multithreading schneller beenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top