Wie führe ich ein einfaches Stück Code in einem neuen Thread aus?

340

Ich habe ein bisschen Code, den ich in einem anderen Thread als der GUI ausführen muss, da das Formular derzeit einfriert, während der Code ausgeführt wird (ca. 10 Sekunden).

Angenommen, ich habe noch nie einen neuen Thread erstellt. Was ist ein einfaches / grundlegendes Beispiel dafür, wie dies in C # und unter Verwendung von .NET Framework 2.0 oder höher durchgeführt wird?

p.campbell
quelle
2
Die meisten Antworten hier waren zu dieser Zeit gut, aber Verbesserungen in .NET Framework 4.0 vereinfachen die Arbeit. Sie können die Task.Run () -Methode verwenden, wie in dieser Antwort beschrieben: stackoverflow.com/a/31778592/1633949
Richard II

Antworten:

336

Ein guter Ort, um mit dem Lesen zu beginnen, ist Joe Albahari .

Wenn Sie einen eigenen Thread erstellen möchten, ist dies so einfach wie möglich:

using System.Threading;
new Thread(() => 
{
    Thread.CurrentThread.IsBackground = true; 
    /* run your code here */ 
    Console.WriteLine("Hello, world"); 
}).Start();
Ed Power
quelle
@EdPower, gilt dies nur für Winforms .. oder funktioniert es in Web Forms ..?
MethodMan
@MethodMan - Ja, es funktioniert in Web Forms. Beginnen Sie hier:
Ed Power
9
Seien Sie vorsichtig bei der Einstellung IsBackgroundauf true. Es macht wahrscheinlich nicht das, was Sie denken. Konfigurieren Sie, ob der Thread beendet wird, wenn alle Vordergrund-Threads gestorben sind, oder ob der Thread die Anwendung am Leben erhält. Wenn Sie nicht möchten, dass Ihr Thread während der Ausführung beendet wird, setzen Sie nicht IsBackgroundauf true.
Zero3
10
@ EdPower Ich denke du solltest auf jeden Fall vorsichtig sein! Ein Beispiel für eine Aufgabe, die Sie während der Ausführung wahrscheinlich nicht beenden möchten, ist das Speichern von Daten auf der Festplatte. Aber sicher, wenn Ihre Aufgabe zu irgendeinem Zeitpunkt zur Beendigung geeignet ist, ist die Flagge in Ordnung. Mein Punkt war nur, dass man vorsichtig mit der Verwendung der Flagge sein muss , da man ihren Zweck nicht beschrieben hat und ihre Benennung leicht zu der Annahme führen könnte, dass sie etwas anderes tut als das, was sie tatsächlich tut.
Zero3
3
Verwenden Sie mit .NET Framework 4.0+ einfach Task.Run (), wie in dieser Antwort beschrieben: stackoverflow.com/a/31778592/1633949
Richard II
193

BackgroundWorker scheint die beste Wahl für Sie zu sein.

Hier ist mein minimales Beispiel. Nachdem Sie auf die Schaltfläche geklickt haben, beginnt der Hintergrundarbeiter mit der Arbeit im Hintergrundthread und meldet gleichzeitig den Fortschritt. Es wird auch nach Abschluss der Arbeiten berichtet.

using System.ComponentModel;
...
    private void button1_Click(object sender, EventArgs e)
    {
        BackgroundWorker bw = new BackgroundWorker();

        // this allows our worker to report progress during work
        bw.WorkerReportsProgress = true;

        // what to do in the background thread
        bw.DoWork += new DoWorkEventHandler(
        delegate(object o, DoWorkEventArgs args)
        {
            BackgroundWorker b = o as BackgroundWorker;

            // do some simple processing for 10 seconds
            for (int i = 1; i <= 10; i++)
            {
                // report the progress in percent
                b.ReportProgress(i * 10);
                Thread.Sleep(1000);
            }

        });

        // what to do when progress changed (update the progress bar for example)
        bw.ProgressChanged += new ProgressChangedEventHandler(
        delegate(object o, ProgressChangedEventArgs args)
        {
            label1.Text = string.Format("{0}% Completed", args.ProgressPercentage);
        });

        // what to do when worker completes its task (notify the user)
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
        delegate(object o, RunWorkerCompletedEventArgs args)
        {
            label1.Text = "Finished!";
        });

        bw.RunWorkerAsync();
    }

Hinweis:

  • Der Einfachheit halber habe ich alles mit der anonymen Methode von C # in einer einzigen Methode zusammengefasst, aber Sie können sie jederzeit auf verschiedene Methoden zurückziehen.
  • Es ist sicher, die GUI innerhalb ProgressChangedoder der RunWorkerCompletedHandler zu aktualisieren . Das Aktualisieren der GUI von DoWork führt jedoch dazu InvalidOperationException.
Gant
quelle
27
Verwenden von System.ComponentModel; (könnte Leute vor einer Google-Suche bewahren, danke für dieses hilfreiche Codebeispiel) +1
sooprise
@Gant Danke für den Beispielcode. Als ich Ihren Code ausprobierte, wurde das ProgressChanged-Ereignis nicht ausgelöst. Als ich jedoch die ähnlich akzeptierte Antwort hier [ stackoverflow.com/questions/23112676/… .
Martin
1
@sooprise Strg +. hilft Ihnen in diesen Situationen! (Vorausgesetzt, Sie verwenden Visual Studio)
September
100

Das ThreadPool.QueueUserWorkItem ist ziemlich ideal für etwas Einfaches. Die einzige Einschränkung ist der Zugriff auf ein Steuerelement vom anderen Thread.

System.Threading.ThreadPool.QueueUserWorkItem(delegate {
    DoSomethingThatDoesntInvolveAControl();
}, null);
Mark Brackett
quelle
Auch mein Favorit. Eine kurze Linie für die Kreuzung . Ich habe es auf Kurzwahl (Snippet). Funktioniert sehr gut mit Steuerelementen. Sie müssen nur wissen, wie Invoke und InvokeRequired verwendet werden .
Bitterblue
1
+1 Beachten Sie, dass Sie mit dem Delegaten die Parameter auch ordentlich umbrechen können.
Will Bickford
Wenn Sie ThreadPool.QueueUserWorkItem mit Rückruffunktion verwenden, werden Sie benachrichtigt, wenn die Aufgabe erledigt ist.
Mou
76

Schnell und schmutzig, aber es wird funktionieren:

Oben verwenden:

using System.Threading;

einfacher Code:

static void Main( string[] args )
{
    Thread t = new Thread( NewThread );
    t.Start();
}

static void NewThread()
{
    //code goes here
}

Ich habe dies zum Beispiel in eine neue Konsolenanwendung geworfen

FallenAvatar
quelle
8
Denken Sie daran, dass mit IsBackground gekennzeichnete Threads von der Laufzeit nicht automatisch beendet werden. Dies erfordert eine Thread-Verwaltung durch die App. Wenn Sie den Thread als nicht im Hintergrund markiert lassen, wird der Thread nach der Ausführung des Threads für Sie beendet.
CmdrTallen
14
@CmdrTallen: Das ist nicht ganz richtig. Ein mit IsBackground = true gekennzeichneter Thread bedeutet, dass der Thread den Prozess nicht am Beenden hindert, dh ein Prozess wird beendet, wenn alle Threads mit IsBackground = false beendet wurden
Phil Devaney
66

Hier ist eine weitere Option:

Task.Run(()=>{
//Here is a new thread
});
SpongeBob-Genosse
quelle
1
Wenn Sie auf diese Weise einen Thread erstellen, wie würden Sie diesen Thread vom UI-Thread abhalten?
Slayernoah
1
@slayernoah Sie können eine CancellationTokenSource als Parameter übergeben und das Stornierungs-Token außerhalb des Threads festlegen
Spongebob-Genosse
7
Dieser Code garantiert nicht, dass Sie einen neuen Thread haben. Die Entscheidung hängt von der aktuellen Implementierung des von Ihnen verwendeten ThreadPool ab. Siehe als Beispiel stackoverflow.com/questions/13570579/…
Giulio Caccin
3
@GiulioCaccin Es ist ThreadPool möglich, einen vorhandenen Thread aus seinem Pool auszuwählen, aber es ist definitiv ein anderer Thread.
SpongeBob Genosse
40

Versuchen Sie es mit der BackgroundWorker- Klasse. Sie geben ihm Delegierte, was ausgeführt werden soll, und werden benachrichtigt, wenn die Arbeit beendet ist. Auf der MSDN-Seite, auf die ich verlinkt habe, befindet sich ein Beispiel.

Andy
quelle
6
Sie können dies sicherlich mit der Thread-Klasse tun, aber BackgroundWorker bietet Ihnen Methoden zur Thread-Vervollständigung und Fortschrittsberichterstattung, die Sie sonst selbst verwenden müssten. Vergessen Sie nicht, dass Sie Invoke verwenden müssen, um mit der Benutzeroberfläche zu sprechen!
Robert Rossney
1
Seien Sie gewarnt: BackgroundWorker hat einige subtile Einschränkungen, aber für das übliche "Ich möchte weggehen und etwas tun und meine Form reagieren lassen" ist es fantastisch.
Merus
10
@Merus, könnten Sie diese subtilen Einschränkungen näher erläutern?
Christian Hudon
14

Wenn Sie einen Wert erhalten möchten:

var someValue;

Thread thread = new Thread(delegate()
            {                 
                //Do somthing and set your value
                someValue = "Hello World";
            });

thread.Start();

while (thread.IsAlive)
  Application.DoEvents();
etw
quelle
6

Fügen Sie diesen Code in eine Funktion ein (den Code, der nicht im selben Thread wie die GUI ausgeführt werden kann), und geben Sie Folgendes ein, um die Ausführung dieses Codes auszulösen.

Thread myThread= new Thread(nameOfFunction);

workerThread.Start();

Wenn Sie die Startfunktion für das Thread-Objekt aufrufen, wird Ihr Funktionsaufruf in einem neuen Thread ausgeführt.

Roter Baron
quelle
3
@ user50612 Worüber sprichst du? Der neue Thread wird nameOfFunctionim Hintergrund und nicht im aktuellen GUI-Thread ausgeführt. Die IsBackgroundEigenschaft bestimmt, ob der Thread die Anwendung am Leben erhält
Zero3
5

Hier erfahren Sie, wie Sie Threads mit einer progressBar verwenden können. Dies dient nur dazu, die Funktionsweise der Threads zu verstehen. In der Form gibt es drei progressBar- und vier Schaltflächen:

 public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    Thread t, t2, t3;
    private void Form1_Load(object sender, EventArgs e)
    {

        CheckForIllegalCrossThreadCalls = false;

         t = new Thread(birinicBar); //evry thread workes with a new progressBar


         t2 = new Thread(ikinciBar);


         t3 = new Thread(ucuncuBar);

    }

    public void birinicBar() //to make progressBar work
    {
        for (int i = 0; i < 100; i++) {
            progressBar1.Value++;
            Thread.Sleep(100); // this progressBar gonna work faster
        }
    }

    public void ikinciBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar2.Value++;
            Thread.Sleep(200);
        }


    }

    public void ucuncuBar()
    {
        for (int i = 0; i < 100; i++)
        {
            progressBar3.Value++;
            Thread.Sleep(300);
        }
    }

    private void button1_Click(object sender, EventArgs e) //that button to start the threads
    {
        t.Start();
        t2.Start(); t3.Start();

    }

    private void button4_Click(object sender, EventArgs e)//that button to stup the threads with the progressBar
    {
        t.Suspend();
        t2.Suspend();
        t3.Suspend();
    }

    private void button2_Click(object sender, EventArgs e)// that is for contuniue after stuping
    {
        t.Resume();
        t2.Resume();
        t3.Resume();
    }

    private void button3_Click(object sender, EventArgs e) // finally with that button you can remove all of the threads
    {
        t.Abort();
        t2.Abort();
        t3.Abort();
    }
}
Ammar Alyasry
quelle
3
// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);

Charles, dein Code (oben) ist nicht korrekt. Sie müssen nicht warten, bis der Vorgang abgeschlossen ist. EndInvoke wird blockiert, bis das WaitHandle signalisiert wird.

Wenn Sie bis zur Fertigstellung blockieren möchten, müssen Sie einfach

nrgDel.EndInvoke(nrgDel.BeginInvoke(lastRuntime,procDT,null,null));

oder alternativ

ar.AsyncWaitHandle.WaitOne();

Aber was bringt es, wenn Sie blockieren, wenn Sie blockieren? Sie können auch einfach einen synchronen Anruf verwenden. Eine bessere Wette wäre, ein Lambda nicht zu blockieren und zur Bereinigung weiterzugeben:

nrgDel.BeginInvoke(lastRuntime,procDT,(ar)=> {ar.EndInvoke(ar);},null);

Eine Sache im Auge zu behalten ist , dass Sie müssen EndInvoke nennen. Viele Leute vergessen dies und verlieren am Ende das WaitHandle, da die meisten asynchronen Implementierungen das Waithandle in EndInvoke freigeben.

Matt Davison
quelle
2

Wenn Sie das rohe Thread-Objekt verwenden möchten, müssen Sie IsBackground mindestens auf true setzen und das Threading Apartment-Modell (wahrscheinlich STA) festlegen.

public static void DoWork()
{
    // do some work
}

public static void StartWorker()
{
    Thread worker = new Thread(DoWork);
    worker.IsBackground = true;
    worker.SetApartmentState(System.Threading.ApartmentState.STA);
    worker.Start()
}

Ich würde die BackgroundWorker-Klasse empfehlen, wenn Sie eine UI-Interaktion benötigen.

user50612
quelle
Seien Sie vorsichtig, wenn Sie IsBackground auf true setzen. Es macht wahrscheinlich nicht das, was Sie denken. Konfigurieren Sie, ob der Thread beendet wird, wenn alle Vordergrund-Threads gestorben sind, oder ob der Thread die Anwendung am Leben erhält. Wenn Sie nicht möchten, dass Ihr Thread während der Ausführung beendet wird, setzen Sie IsBackground nicht auf true.
Zero3
1

Eine weitere Option, die Delegaten und den Thread-Pool verwendet ...

Angenommen, 'GetEnergyUsage' ist eine Methode, die eine DateTime und eine andere DateTime als Eingabeargumente verwendet und eine Int ...

// following declaration of delegate ,,,
public delegate long GetEnergyUsageDelegate(DateTime lastRunTime, 
                                            DateTime procDateTime);

// following inside of some client method 
GetEnergyUsageDelegate nrgDel = GetEnergyUsage;                     
IAsyncResult aR = nrgDel.BeginInvoke(lastRunTime, procDT, null, null);
while (!aR.IsCompleted) Thread.Sleep(500);
int usageCnt = nrgDel.EndInvoke(aR);
Charles Bretana
quelle
1
Charles, unterscheidet sich Ihr Code funktional von int usageCnt = nrgDel.Invoke(lastRunTime, procDT, null, null);? Es sieht so aus, als würde der aktuelle Thread ohnehin mit dem Ruhezustand angehalten ... Ich dachte, es hilft nichts beim Einfrieren der GUI, wenn Sie ihn im GUI-Thread
aufrufen
Ja, BeginInvoke ruft den Delegaten in einem anderen Thread aus dem Thread-Pool auf ... Wenn Sie Invoke verwenden, wird der Delegat im aktuellen Thread synchron aufgerufen ... Aber Sie haben Recht, der Schlaf ist falsch ... Sie sollten das und beseitigen Sammeln Sie die Ergebnisse mit einer Rückruffunktion. Ich werde bearbeiten, um das zu zeigen
Charles Bretana
1

Es gibt viele Möglichkeiten, separate Threads in .Net auszuführen. Jedes hat unterschiedliche Verhaltensweisen. Müssen Sie den Thread nach dem Beenden der GUI weiter ausführen? Müssen Sie Informationen zwischen dem Thread und der GUI weitergeben? Muss der Thread die GUI aktualisieren? Sollte der Thread eine Aufgabe ausführen und dann beenden oder sollte er weiter ausgeführt werden? Die Antworten auf diese Fragen zeigen Ihnen, welche Methode Sie verwenden müssen.

Auf der Code Project-Website finden Sie einen guten Artikel über asynchrone Methoden, in dem die verschiedenen Methoden beschrieben und Beispielcode bereitgestellt werden.

Beachten Sie, dass dieser Artikel geschrieben wurde, bevor das asynchrone / wartende Muster und die Task Parallel Library in .NET eingeführt wurden.

Dour High Arch
quelle
Dies ist die Art von Informationen, nach denen ich gesucht habe, aber Ihre Antwort ist ein Opfer von Link Rot.
Chris
Danke, dass du mich informiert hast, @Chris; Link behoben.
Dour High Arch
0

Gewusst wie: Verwenden Sie einen Hintergrund-Thread, um nach Dateien zu suchen

Sie müssen beim Zugriff von anderen Threads auf GUI-spezifische Inhalte sehr vorsichtig sein (dies ist bei vielen GUI-Toolkits üblich). Wenn Sie etwas in der GUI über den Verarbeitungsthread aktualisieren möchten, überprüfen Sie diese Antwort , die meiner Meinung nach für WinForms nützlich ist. Für WPF siehe dies (es zeigt, wie man eine Komponente in der UpdateProgress () -Methode berührt, damit sie von anderen Threads aus funktioniert, aber eigentlich gefällt es mir nicht, dass es nicht funktioniert, CheckAccess()bevor man BeginInvokeDispathcer durchläuft, siehe CheckAccess darin und sucht danach).

Ich habe ein .NET-spezifisches Buch zum Thema Threading gesucht und dieses gefunden (kostenlos herunterladbar). Siehe http://www.albahari.com/threading/ , um weitere Informationen darüber.

Ich glaube, Sie werden auf den ersten 20 Seiten finden, was Sie brauchen, um die Ausführung als neuer Thread zu starten, und es gibt noch viel mehr (nicht sicher über GUI-spezifische Snippets, die ich streng spezifisch für das Threading meine). Würde mich freuen zu hören, was die Community über diese Arbeit denkt, denn ich lese diese. Im Moment sah es für mich ziemlich ordentlich aus (um .NET-spezifische Methoden und Typen für das Threading anzuzeigen). Außerdem behandelt es .NET 2.0 (und nicht das alte 1.1), was ich wirklich schätze.

IgorK
quelle
0

Ich würde empfehlen, sich Jeff Richters Power Threading Library und speziell den IAsyncEnumerator anzusehen. Schauen Sie sich das Video zu Charlie Calvert an Blog an, in dem Richter einen guten Überblick darüber gibt.

Lassen Sie sich nicht vom Namen abschrecken, da dadurch asynchrone Programmieraufgaben einfacher zu codieren sind.

Robert Paulson
quelle