GCD zur Ausführung der Aufgabe im Hauptthread


254

Ich habe einen Rückruf, der von jedem Thread kommen könnte. Wenn ich diesen Rückruf erhalte, möchte ich eine bestimmte Aufgabe im Hauptthread ausführen.

Muss ich überprüfen, ob ich bereits im Haupt-Thread bin - oder gibt es eine Strafe, wenn ich diese Überprüfung nicht durchführe, bevor ich den folgenden Code aufrufe?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});

116
Fünf Jahre später kann ich mich immer noch nicht an die Syntax der GCD-Blöcke erinnern und lande jedes Mal hier.
SpaceTrucker

7
@ SpaceTrucker - Das ist der gleiche Grund, warum ich auf dieser Seite bin: D
Matthew Cawley

4
9 Jahre später, und ich komme immer noch, um die Syntax von dieser Seite zu kopieren.
Osa

Antworten:


152

Nein, Sie müssen nicht überprüfen, ob Sie bereits im Hauptthread sind. Wenn Sie den Block an die Hauptwarteschlange senden, planen Sie lediglich, dass der Block seriell im Hauptthread ausgeführt wird. Dies geschieht, wenn die entsprechende Ausführungsschleife ausgeführt wird.

Wenn Sie sich bereits im Hauptthread befinden, ist das Verhalten dasselbe: Der Block wird geplant und ausgeführt, wenn die Ausführungsschleife des Hauptthreads ausgeführt wird.


3
Die Frage war, ob es eine "Strafe gibt, wenn diese Prüfung nicht durchgeführt wird" ... Ich würde denken, dass es eine Leistungsstrafe für die Verwendung von asynchronem Versand gibt, wenn dies nicht erforderlich ist, oder ist es trivial?
Dan Rosenstark

1
@Yar Ich glaube nicht, dass es in den meisten Fällen spürbare Auswirkungen auf die Leistung gibt: GCD ist eine leichte Bibliothek. Trotzdem verstand ich die Frage wie folgt: "Muss ich angesichts des folgenden Codes überprüfen, ob ich im Haupt-Thread bin?"

7
Sie müssen jedoch überprüfen, ob Sie dispatch_sync verwenden. Andernfalls kommt es zu einem Deadlock.
Victor Engel

Wenn Sie sich in der Hauptwarteschlange befinden und asyncwieder in die Hauptwarteschlange zurückkehren, wird diese ausgeführt. Dies kann jedoch den erwarteten Zeitpunkt Ihrer Aktionen beeinträchtigen . Beispielsweise wird der UI-Code viewDidLoad() erst ausgeführt, nachdem die Ansicht zum ersten Mal angezeigt wurde .
Pkamb

106

Für den oben beschriebenen asynchronen Versandfall sollten Sie nicht überprüfen müssen, ob Sie sich im Hauptthread befinden. Wie Bavarious angibt, wird dies einfach in die Warteschlange gestellt, um auf dem Hauptthread ausgeführt zu werden.

Wenn Sie jedoch versuchen, die oben genannten Schritte mit a dispatch_sync()auszuführen, und sich Ihr Rückruf im Hauptthread befindet, wird Ihre Anwendung an diesem Punkt blockiert. Ich beschreibe dies in meiner Antwort hier , weil mich dieses Verhalten beim Verschieben von Code überrascht hat -performSelectorOnMainThread:. Wie ich dort erwähne, habe ich eine Hilfsfunktion erstellt:

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

Dadurch wird ein Block synchron im Hauptthread ausgeführt, wenn sich die Methode, in der Sie sich befinden, derzeit nicht im Hauptthread befindet, und der Block wird nur inline ausgeführt, wenn dies der Fall ist. Sie können eine Syntax wie die folgende verwenden, um dies zu verwenden:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

1
Warum nennst du es nicht so etwas wie "runOnMainQueueSync"? Die Tatsache, dass es zum Stillstand kommen könnte und nicht, ist die Art von Dingen, die ich nicht überall in meinem Code haben möchte. Danke und +1 wie immer.
Dan Rosenstark

2
@Yar - Ich habe ihm nur diesen Namen gegeben, damit mir klar wurde, warum ich nicht einfach Blöcke synchron in der Hauptwarteschlange abfeuerte, anstatt diese Hilfsfunktion zu verwenden. Es war eher eine Erinnerung an mich.
Brad Larson

3
Es ist weiterhin möglich, mit dieser Funktion einen Deadlock durchzuführen. Synchronisierung der Hauptwarteschlange> Synchronisierung der anderen Warteschlange> Hauptwarteschlange blockiert.
Hfossli

2
@hfossli - Stimmt, es wird nicht jeden Fall behandeln. In meiner Verwendung habe ich immer entweder von einem asynchronen Versand in einer seriellen Hintergrundwarteschlange oder von Inline-Code im Hauptthread angerufen. Ich frage mich, ob dispatch_set_specific()dies in dem von Ihnen beschriebenen Fall hilfreich wäre: stackoverflow.com/a/12806754/19679 .
Brad Larson

1
[NSThread isMainThread] gibt häufig YES zurück und wird bei der GCD-Programmierung nicht als sicher für die Überprüfung dieses Falls angesehen. stackoverflow.com/questions/14716334/…
Will Larche

57

Wie in den anderen Antworten erwähnt, ist dispatch_async vom Hauptthread in Ordnung.

Abhängig von Ihrem Anwendungsfall gibt es jedoch einen Nebeneffekt, den Sie möglicherweise als Nachteil betrachten: Da der Block in einer Warteschlange geplant ist, wird er erst ausgeführt, wenn die Steuerung zur Ausführungsschleife zurückkehrt, was zu einer Verzögerung führt die Ausführung Ihres Blocks.

Beispielsweise,

NSLog(@"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"inside dispatch async block main thread from main thread");
});
NSLog(@"after dispatch async");

Wird ausgedruckt:

before dispatch async
after dispatch async
inside dispatch async block main thread from main thread

Aus diesem Grund würde dispatch_async Ihnen nicht helfen, wenn Sie erwarten würden, dass der Block zwischen den äußeren NSLogs ausgeführt wird.


Muss sagen, dass dies eine wichtige Sache zu berücksichtigen ist
Marc-Alexandre Bérubé

Da Sie überprüfen möchten, bedeutet dies, dass der Code möglicherweise im Hauptthread ausgeführt wird oder nicht. Bereits im Hauptthread wird in dieser Reihenfolge ausgeführt, andernfalls kann dies nicht garantiert werden. Sie sollten daher vermeiden , den Code so zu gestalten, dass er in einer bestimmten Reihenfolge ausgeführt wird, wenn Sie sich auf die Multithread-Programmierung beziehen. Versuchen Sie, den asynchronen Rückruf zu verwenden.
ooops

1

Nein, Sie müssen nicht überprüfen, ob Sie sich im Haupt-Thread befinden. So können Sie dies in Swift tun:

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.