3 Stimmen

Verständnis BackgroundWorker

Ich möchte verstehen, wie BackgroundWorker verwendet wird. Ich habe versucht, es in ein Console C#-Projekt zu zerlegen, tatsächlich handelt es sich um eine Windows Form-Anwendung, bei der eine Schaltfläche die Ausführung von drei Hintergrundtasks auslöst. Wenn die Schaltfläche gedrückt wird, sollte sie deaktiviert werden, um weitere Klicks zu verhindern. Wenn alle drei Tasks abgeschlossen sind, sollte die Schaltfläche wieder aktiviert werden. Außerdem sollte der Erfolg der drei Tasks im Hauptthread getestet werden. Um zu verhindern, dass dies alles in eine Forms-App gemischt wird, versuche ich nun, die Grundlagen zu verstehen und das in die Forms-Anwendung zu übertragen. (Aus den Kommentareinstellungen können Sie erraten, wo meine Verständnisprobleme liegen) Überlegen Sie diesen (immer noch fehlerhaften) Code:

using System;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;

namespace BGWorkerConsoleApp
{
    class Program
    {

//        #region BackgroundWorker
//
private System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();

//
//        #endregion
        static void Main(string[] args)
        {
//            BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();

            worker.RunWorkerCompleted += TestComplete;
            worker.DoWork += TestConnection1;
//            worker.DoWork += TestConnection2;
//            worker.DoWork += TestConnection3;
            DoWorkEventArgs e = new DoWorkEventArgs(); // ???

            worker.RunWorkerAsync(e); // ???
        }

        private void TestConnection1(object sender, DoWorkEventArgs e)
        {
            bool success = false;
            Thread.Sleep(14000); // steht für einen zeitaufwändigen Task
            success = true;
            e.Result = success;
        }
        private void TestConnection2(object sender, DoWorkEventArgs e)
        {
            bool success = false;
            Thread.Sleep(10000); // steht für einen zeitaufwändigen Task
            success = true;
            e.Result = success;
        }
        private void TestConnection3(object sender, DoWorkEventArgs e)
        {
            bool success = false;
            Thread.Sleep(5000); // steht für einen zeitaufwändigen Task
            success = true;
            e.Result = success;
        }

        private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
        {
            Console.WriteLine("complete");
        }

    }
}

Meine Fragen:

Kann ich mehr als eine Aufgabe einem BackgroundWorker hinzufügen? Wahrscheinlich nicht, da es nur ein RunWorkerAsync gibt. Angenommen, ich brauche einen Worker für jede Aufgabe, wie würde ich auf das Abschließen aller drei Aufgaben warten?

OK, ich möchte es jetzt zu einer Forms-Anwendung ändern, da der Schwerpunkt nicht darauf liegt, BackgroundWorker in einer Konsolen-App auszuführen, sondern vielmehr möchte ich verstehen, wie die Anwendung für mehrere BackgroundWorker entworfen werden kann und auf deren Abschluss wartet (einschließlich der Übergabe und Rückgabe von Parametern und Ergebnissen).

Hier ist der Code dafür in einer Form-Anwendung:

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;

namespace BackGroundWorkerForm
{
    partial class Form1
    {
        /// 
        /// Erforderliche Designer-Variable.
        /// 
        private System.ComponentModel.IContainer components = null;

        private Button button;
        /// 
        /// Bereinigen aller verwendeten Ressourcen.
        /// 
        /// true, wenn verwaltete Ressourcen freigegeben werden sollen; andernfalls false.
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Formular-Designer generierter Code

        /// 
        /// Erforderliche Methode für die Designerunterstützung. Verändern Sie
        /// den Inhalt dieser Methode nicht mit dem Code-Editor.
        /// 
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Text = "Form1";
            button = new System.Windows.Forms.Button();
            button.Location = new System.Drawing.Point(100, 10);
            button.Name = "button";
            button.Size = new System.Drawing.Size(100, 20);
            button.TabIndex = 5;
            button.Text = "TestConnection";
            button.Click += new System.EventHandler(this.RunTest);
            button.Visible = true;
            Controls.Add(button);
        }

        #endregion

       private void RunTest(object o, EventArgs e)
        {
        BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
        worker.RunWorkerCompleted += TestComplete;
        worker.DoWork += TestConnection1;
//            worker.DoWork += TestConnection2;
//            worker.DoWork += TestConnection3;

        worker.RunWorkerAsync();
        }   
        private void TestConnection1(object sender, DoWorkEventArgs e)
        {
            bool success = false;
            Thread.Sleep(10000); // steht für einen zeitaufwändigen Task
            success = true;
            e.Result = success;
        }
        private void TestConnection2(object sender, DoWorkEventArgs e)
        {
            bool success = false;
            Thread.Sleep(10000); // steht für einen zeitaufwändigen Task
            success = true;
            e.Result = success;
        }
        private void TestConnection3(object sender, DoWorkEventArgs e)
        {
            bool success = false;
            Thread.Sleep(10000); // steht für einen zeitaufwändigen Task
            success = true;
            e.Result = success;
        }

        private void TestComplete(object sender, RunWorkerCompletedEventArgs e)
        {
            button.Text= "complete";
        }

    }
}

1voto

Chad Dienhart Punkte 4874

Hier ist ein Beispiel für die Verwendung von Tasks anstelle eines Hintergrund-Arbeiter. Jede Task wird asynchron gestartet und wartet dann darauf, dass alle erfolgreich oder nicht erfolgreich abgeschlossen wurden, bevor die Ergebnisse verarbeitet werden.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BGWorkerConsoleApp
{
    class Program
    {
        static Stopwatch sw = new Stopwatch();
        static void Main(string[] args)
        {
            // Analysieren Sie Ihre Argumente
            int[] parsedargs = { 1400, 1000, 500 };
            int count = 0; // um die Anzahl der Tasks zu liefern
            List tasks = new List();
            sw.Start(); // Stoppuhr für einige zur Überprüfung der Zeit
            foreach (int i in parsedargs)
            {
                // Starten Sie eine Task für jedes
                tasks.Add(Task.Factory.StartNew(
                    () => { return myTask(i, String.Format("Task{0} erledigt.  ", ++count)); }));
            }

            // Warten Sie darauf, dass alle Tasks abgeschlossen sind
            Task.WaitAll(tasks.ToArray());

            // Überprüfen Sie die Antwort von jedem
            bool faulted = false;
            foreach (Task t in tasks)
            {
                if (t.Result == false)
                {
                    faulted = true;
                    break; // Es gab ein Problem, also hören wir auf zu suchen
                }
            }

            // gib etwas Text aus
            if (faulted)
            {
                Console.WriteLine("Es gab ein Problem.");
            }
            else
                Console.WriteLine("Vollständig");

            // Pause, damit wir unsere Ausgabe im Debugger sehen können
            Console.ReadKey();
        }

        static bool myTask(int time, string msg)
        {
            Thread.Sleep(time);
            if (time == 1000)
                return false; 
            Console.WriteLine(msg + printStopWatchTime());
            return true;
        }

        static string printStopWatchTime()
        {
            TimeSpan ts = sw.Elapsed;
            string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds / 10);
            return string.Format("Laufzeit {0}", elapsedTime);
        }
    }
}

0voto

Leandro Punkte 1526

Für Ihre erste Frage: Sie können einen BackgroundWorker wiederverwenden, solange Sie versuchen, die Aufgabe nicht auszuführen, während sie bereits läuft (d.h. IsBusy muss gleich false sein). Laut MSDN, wenn Sie das versuchen, wird es Sie hart beißen mit einer InvalidOperationException.

Zum zweiten Thema: Sie benötigen eine Art von Synchronisationsmechanismus, um das zu erreichen. Überprüfen Sie WaitHandle.WaitAll(...) und die Barrier-Klasse, zum Beispiel. Es gibt viele Optionen, nicht nur diese. Sie haben Monitore, Semaphoren, Mutexe und was auch immer zu Ihrer Verfügung. Erforschen Sie den System.Threading-Namespace.

0voto

Thorsten Dittmar Punkte 54284

Ein BackgroundWorker ist gut, wenn Sie eine spezifische Aufgabe im Hintergrund ausführen möchten. Das heißt, das DoWork-Ereignis ist einem Handler zugewiesen, der die Aufgabe ausführt und beendet. Natürlich können Sie die Ereignismethode ändern, indem Sie die alte entfernen und eine neue zuweisen, aber der wichtige Teil ist, dass nur eine Aufgabe gleichzeitig ausgeführt werden kann (Sie können nicht den gleichen Hintergrundarbeiter mehrmals gleichzeitig ausführen lassen).

Wenn Sie möchten, dass zwei oder mehr Aufgaben gleichzeitig ausgeführt werden, benötigen Sie entweder mehr Background-Arbeiter oder Sie beginnen, sich mit der Task Parallel Library zu beschäftigen, wie in den Kommentaren vorgeschlagen. Sogar einfache Threads würden funktionieren.

In jedem Fall, wie Leandro in seiner Antwort bereits sagte, schreit Ihre Zielsetzung nach der Verwendung einer Barriere, da sonst die zuerst beendete Aufgabe die Schaltfläche aktiviert, bevor die anderen Aufgaben beendet sind. Sie möchten jedoch warten, bis alle Aufgaben beendet sind.

Ich würde hinzufügen, dass meiner Meinung nach die Anwendungsfälle für den BackgroundWorker ziemlich begrenzt sind. Während es in einigen Fällen praktisch ist, bietet es nicht die Flexibilität, die in den meisten Fällen erforderlich ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X