Schlafaufgabe (System.Threading.Tasks)

7

Ich muss einen Thread erstellen, der das Foto im Fenster Windows Forms ersetzt, und dann auf ~ 1 Sekunde und das vorherige Foto wiederherstellen.

Ich dachte, dass der folgende Code:

%Vor%

mache den Job, aber leider nicht. Es friert den Haupt-UI-Thread ein.

Das liegt daran, dass nicht garantiert ist, dass es einen Thread pro Aufgabe gibt. Ein Thread kann zur Bearbeitung mehrerer Aufgaben verwendet werden. Sogar TaskCreationOptions.LongRunning Option kann Hilf mir nicht.

Wie kann ich es beheben?

    
patryk.beza 10.04.2012, 22:54
quelle

3 Antworten

25

Thread.Sleep ist eine synchrone Verzögerung . Wenn Sie eine asynchrone Verzögerung möchten, verwenden Sie < em> Aufgabe.Delay .

In C # 5, das derzeit in der Beta-Version ist, können Sie einfach

sagen %Vor%

in einer asynchronen Methode, und die Methode wird automatisch dort fortgesetzt, wo sie aufgehört hat.

Wenn Sie nicht C # 5 verwenden, können Sie "manuell" den Code einstellen, den Sie selbst als Fortsetzung der Verzögerung verwenden möchten.

    
Eric Lippert 11.04.2012, 04:31
quelle
7

Wenn Sie einen neuen TaskScheduler übergeben, der aus dem aktuellen Synchronisationskontext stammt, weisen Sie die Task an, dass sie im UI-Thread ausgeführt werden soll. Sie möchten das tatsächlich tun, also können Sie die UI-Komponente aktualisieren, jedoch möchten Sie nicht in diesem Thread schlafen, da es blockieren wird.

Dies ist ein gutes Beispiel dafür, wenn .ContinueWith ideal ist:

%Vor%

BEARBEITEN (Etwas entfernt und hinzugefügt):

In diesem Fall blockieren wir den UI-Thread nur so lange, bis pic.Image aktualisiert wurde. Wenn Sie TaskScheduler angeben, sagen Sie ihm, in welchem ​​Thread die Aufgabe ausgeführt werden soll. Es ist wichtig zu wissen, dass die Beziehung zwischen Aufgaben und Threads nicht 1: 1 ist. Tatsächlich können 1000 Aufgaben mit relativ wenigen Threads ausgeführt werden, 10 oder weniger sogar, alles hängt von der Menge an Arbeit ab, die jede Aufgabe hat. Gehen Sie nicht davon aus, dass jede Aufgabe, die Sie erstellen, in einem separaten Thread ausgeführt wird. Die CLR leistet einen hervorragenden Job, um die Leistung automatisch für Sie auszugleichen.

Nun müssen Sie nicht mehr den Standard TaskScheduler verwenden, wie Sie gesehen haben. Wenn Sie die UI TaskScheduler übergeben, also TaskScheduler.FromCurrentSynchronizationContext() , verwendet sie den UI-Thread anstelle des Thread-Pools, wie es TaskScheduler.Default tut.

Denken wir daran, den Code noch einmal zu überprüfen:

%Vor%

Hier erstellen und starten wir eine Aufgabe, die auf dem Thread UI ausgeführt wird und die Eigenschaft Image von pic mit Ihrer Ressource aktualisiert. Während dies geschieht, reagiert die UI nicht mehr. Glücklicherweise ist dies eine sehr schnelle Operation, die der Benutzer nicht bemerkt.

%Vor%

Mit diesem Code rufen wir die Methode ContinueWith auf. Es macht genau das, wonach es sich anhört. Es gibt ein neues Task -Objekt zurück, das den lambda-Parameter ausführt, wenn es ausgeführt wird. Es wird gestartet, wenn die Aufgabe abgeschlossen, fehlerhaft oder abgebrochen wurde. Sie können steuern, wann es ausgeführt wird, indem Sie TaskContinuationOptions übergeben. Wir übergeben jedoch auch einen anderen Taskplaner wie zuvor. Dies ist der Standard-Aufgabenplaner, der eine Aufgabe in einem Thread-Pool-Thread ausführt und somit die Benutzeroberfläche NICHT blockiert. Diese Aufgabe kann stundenlang ausgeführt werden und Ihre Benutzeroberfläche bleibt reaktiv (lassen Sie es nicht zu), da es sich um einen separaten Thread handelt, mit dem Sie interagieren.

Wir haben auch ContinueWith für die Aufgaben aufgerufen, die wir für die Ausführung im Standard-Taskplaner festgelegt haben. Dies ist die Aufgabe, die das Bild im Benutzeroberflächenthread erneut aktualisiert, da wir denselben Taskplaner für die Benutzeroberfläche an die ausführende Task übergeben haben. Sobald der Threadpool-Task beendet ist, ruft er diesen im UI-Thread auf und blockiert ihn für eine sehr kurze Zeit, während das Image aktualisiert wird.

    
Christopher Currens 10.04.2012 23:07
quelle
5

Sie sollten Timer verwenden, um zu einem späteren Zeitpunkt eine UI-Aufgabe auszuführen. Stellen Sie es so ein, dass es einmal und in einem Intervall von 1 Sekunde läuft. Setzen Sie den UI-Code in das Tick-Ereignis und setzen Sie ihn dann ab.

Wenn Sie wirklich Aufgaben verwenden möchten, möchten Sie, dass die andere Aufgabe nicht im Benutzeroberflächen-Thread ausgeführt wird, sondern in einer Hintergrundbedrohung (dh nur eine normale StartNew Aufgabe) und Verwenden Sie dann das Control.Invoke innerhalb der Aufgabe, um einen Befehl im UI-Thread auszuführen. Das Problem dabei ist, dass das zugrundeliegende Problem, eine Aufgabe zu beginnen, nur um es schlafen zu lassen, band-assistiert wird. Besser, wenn der Code nicht einmal für die volle Sekunde ausgeführt wird.

    
Servy 10.04.2012 23:03
quelle