iPhone - Hauptthread von Grand Central Dispatch

145

Ich habe mit Erfolg Grand Central Dispatch in meinen Apps verwendet, aber ich habe mich gefragt, was der wahre Vorteil der Verwendung von so etwas ist:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

oder auch

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

Ich meine, in beiden Fällen feuern Sie einen Block ab, der im Hauptthread ausgeführt werden soll, genau dort, wo die App ausgeführt wird, und dies trägt nicht dazu bei, die Last zu reduzieren. Im ersten Fall haben Sie keine Kontrolle darüber, wann der Block ausgeführt wird. Ich habe Fälle gesehen, in denen Blöcke eine halbe Sekunde nach dem Abfeuern ausgeführt wurden. Der zweite Fall ist ähnlich wie

[self doStuff];

richtig?

Ich frage mich, was ihr denkt.

Ente
quelle
9
Das Werfen einer Hauptwarteschlange in eine dispatch_sync führt übrigens zu einem Deadlock.
Brooks Hanes
5
Lesen Sie es einfach in den Dokumenten: "Im Gegensatz zu dispatch_async kehrt [dispatch_sync] erst zurück, wenn der Block beendet ist. Das Aufrufen dieser Funktion und das Targeting der aktuellen Warteschlange führt zu einem Deadlock." ... Aber vielleicht lese ich das falsch ... ( Die aktuelle Warteschlange bedeutet nicht Hauptthread. Bitte korrigieren Sie, wenn ich falsch liege.
Brooks Hanes
4
@BrooksHanes nicht immer wahr. Dies führt zu einem Deadlock, wenn Sie sich bereits im Haupt-Thread befinden. Wenn nicht, würde es keinen Deadlock geben. Siehe hier
Honey

Antworten:

296

Das Versenden eines Blocks in die Hauptwarteschlange erfolgt normalerweise aus einer Hintergrundwarteschlange, um zu signalisieren, dass einige Hintergrundverarbeitungen abgeschlossen sind, z

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

In diesem Fall führen wir eine lange Berechnung in einer Hintergrundwarteschlange durch und müssen unsere Benutzeroberfläche aktualisieren, wenn die Berechnung abgeschlossen ist. Das Aktualisieren der Benutzeroberfläche muss normalerweise von der Hauptwarteschlange aus erfolgen, damit wir mit einem zweiten verschachtelten dispatch_async ein Signal an die Hauptwarteschlange senden.

Es gibt wahrscheinlich andere Beispiele, bei denen Sie möglicherweise an die Hauptwarteschlange zurücksenden möchten, dies geschieht jedoch im Allgemeinen auf diese Weise, dh innerhalb eines Blocks, der an eine Hintergrundwarteschlange gesendet wird.

  • Hintergrundverarbeitung beendet -> Benutzeroberfläche aktualisieren
  • Datenblock in der Hintergrundwarteschlange verarbeitet -> Hauptwarteschlange signalisieren, um den nächsten Block zu starten
  • Eingehende Netzwerkdaten in der Hintergrundwarteschlange -> Hauptwarteschlange signalisieren, dass die Nachricht angekommen ist
  • etc etc.

Warum möchten Sie möglicherweise von der Hauptwarteschlange aus in die Hauptwarteschlange versenden ? Nun, im Allgemeinen würden Sie dies nicht tun, obwohl Sie dies möglicherweise tun könnten, um einige Arbeiten für das nächste Mal in der Ausführungsschleife zu planen.

Robin Summerhill
quelle
Ah ich sehe. Also ich habe recht. Dies hat keinen Vorteil, wenn Sie sich bereits in der Hauptwarteschlange befinden, nur wenn Sie sich in einer anderen Warteschlange befinden und die Benutzeroberfläche aktualisieren möchten. Vielen Dank.
Ente
Ich habe gerade meine Antwort bearbeitet, um darüber zu sprechen, warum es nicht sehr nützlich ist, dies in der Hauptwarteschlange zu tun.
Robin Summerhill
Ich denke auch, dass es in iOS 4 einen Fehler gibt (möglicherweise in iOS 5), bei dem dispatch_sync in die Hauptwarteschlange vom Hauptthread nur einen Hang verursacht, sodass ich dies ganz vermeiden würde.
Joerick
10
Das ist kein Fehler, das ist das erwartete Verhalten. Zugegebenermaßen kein sehr nützliches Verhalten, aber Sie müssen bei der Verwendung von dispatch_sync immer auf Deadlocks achten. Sie können nicht erwarten, dass das System Sie ständig vor Programmiererfehlern schützt.
Robin Summerhill
2
Was ist backgroundQueue hier? Wie erstelle ich ein backgroundQueue-Objekt
Nilesh Tupe
16

Das Versenden von Blöcken aus dem Hauptthread in die Hauptwarteschlange kann hilfreich sein. Dadurch kann die Hauptwarteschlange andere Blöcke verarbeiten, die in die Warteschlange gestellt wurden, sodass Sie nicht einfach die Ausführung aller anderen Blöcke blockieren.

Sie könnten beispielsweise einen Server mit einem einzigen Thread schreiben, der dennoch viele gleichzeitige Verbindungen verarbeitet. Solange kein einzelner Block in der Warteschlange zu lange dauert, reagiert der Server auf neue Anforderungen.

Wenn Ihr Programm nichts anderes tut, als sein ganzes Leben damit zu verbringen, auf Ereignisse zu reagieren, kann dies ganz natürlich sein. Sie müssen nur Ihre Ereignishandler so einrichten, dass sie in der Hauptwarteschlange ausgeführt werden, und dann dispatch_main () aufrufen, und Sie müssen sich möglicherweise überhaupt nicht um die Thread-Sicherheit kümmern.

bames53
quelle
11

Hoffentlich verstehe ich Ihre Frage richtig, indem Sie sich über die Unterschiede zwischen dispatch_async und dispatch_sync wundern?

dispatch_async

wird den Block asynchron an eine Warteschlange senden. Dies bedeutet, dass der Block an die Warteschlange gesendet wird und nicht auf die Rückkehr gewartet wird, bevor mit der Ausführung des verbleibenden Codes in Ihrer Methode fortgefahren wird.

dispatch_sync

wird den Block synchron an eine Warteschlange senden. Dadurch wird verhindert, dass der verbleibende Code in der Methode weiter ausgeführt wird, bis die Ausführung des Blocks abgeschlossen ist.

Ich habe meistens dispatch_asynceine Hintergrundwarteschlange verwendet, um die Arbeit aus der Hauptwarteschlange zu entfernen und die zusätzlichen Kerne des Geräts zu nutzen. Dann dispatch_asynczum Haupt-Thread, wenn ich die Benutzeroberfläche aktualisieren muss.

Viel Glück

timthetoolman
quelle
1
danke, aber ich frage nach den Vorteilen, wenn man etwas an die Hauptwarteschlange sendet und sich in der Hauptwarteschlange befindet.
Ente
9

Ein Ort, an dem es nützlich ist, ist für UI-Aktivitäten wie das Einstellen eines Spinners vor einer längeren Operation:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

funktioniert nicht, weil Sie den Haupt-Thread während Ihrer langen Arbeit blockieren und UIKit den Spinner nicht starten lässt.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

Die Steuerung wird an die Ausführungsschleife zurückgegeben, die die Aktualisierung der Benutzeroberfläche plant, den Spinner startet und dann das nächste Element aus der Versandwarteschlange entfernt, bei dem es sich um Ihre eigentliche Verarbeitung handelt. Wenn Ihre Verarbeitung abgeschlossen ist, wird der Animationsstopp aufgerufen und Sie kehren zur Ausführungsschleife zurück, in der die Benutzeroberfläche mit dem Stopp aktualisiert wird.

Wieselfloss1
quelle
@ Jerceratops ja, aber es ermöglicht den Abschluss des aktuellen Runloops.
Dan Rosenstark
3
Ja, aber es ist immer noch schrecklich. Die Benutzeroberfläche wird weiterhin blockiert. Ich könnte gleich nach diesem einen anderen Knopf drücken. Oder versuchen Sie zu scrollen. "(mach etwas langwieriges)" sollte im Hauptthread nicht passieren, und dispatch_async, damit die Schaltfläche auf "Fertig stellen" klickt, ist keine akzeptable Lösung.
Jerceratops
8

Swift 3, 4 & 5

Ausführen von Code im Hauptthread

DispatchQueue.main.async {
    // Your code here
}
Niall Kiddle
quelle
1

Async bedeutet asynchron und sollte meistens verwendet werden. Sie sollten niemals die Synchronisierung im Hauptthread aufrufen, da dies Ihre Benutzeroberfläche blockiert, bis die Aufgabe abgeschlossen ist. Sie Hier ist ein besserer Weg, dies in Swift zu tun:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Es ist als Standardfunktion in meinem Repo enthalten. Schauen Sie sich das an: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
quelle