Warten Sie, bis zwei asynchrone Blöcke ausgeführt wurden, bevor Sie einen weiteren Block starten


192

Bei Verwendung von GCD möchten wir warten, bis zwei asynchrone Blöcke ausgeführt und ausgeführt wurden, bevor wir mit den nächsten Ausführungsschritten fortfahren. Was ist der beste Weg das zu tun?

Wir haben Folgendes versucht, aber es scheint nicht zu funktionieren:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
});


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
});

// wait until both the block1 and block2 are done before start block3
// how to do that?

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
});

Siehe meine Antwort für Swift 5, die bis zu sechs verschiedene Möglichkeiten zur Lösung Ihres Problems bietet.
Imanou Petit

Antworten:


301

Verwenden Versandgruppen: siehe hier für ein Beispiel, „Waiting auf Gruppen von Queued Aufgaben“ in den „Dispatch - Queues“ Kapiteln von Apples iOS Developer Library Concurrency Programming Guide

Ihr Beispiel könnte ungefähr so ​​aussehen:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
    NSLog(@"Block1");
    [NSThread sleepForTimeInterval:5.0];
    NSLog(@"Block1 End");
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
    NSLog(@"Block2");
    [NSThread sleepForTimeInterval:8.0];
    NSLog(@"Block2 End");
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
    NSLog(@"Block3");
});

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group);

und könnte eine Ausgabe wie diese erzeugen:

2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3

3
Cool. Werden die asynchronen Aufgaben / Blöcke, sobald sie der Gruppe zugeordnet sind, nacheinander oder gleichzeitig ausgeführt? Ich meine, nehmen wir an, dass Block1 und Block2 jetzt einer Gruppe zugeordnet sind. Wird Block2 warten, bis Block1 fertig ist, bevor er ausgeführt werden kann?
Tom

9
Das liegt an dir. dispatch_group_asyncist genau wie dispatch_asyncmit einem Gruppenparameter hinzugefügt. Wenn Sie also unterschiedliche Warteschlangen für Block1 und Block2 verwenden oder diese in derselben gleichzeitigen Warteschlange planen, können sie gleichzeitig ausgeführt werden. Wenn Sie sie in derselben seriellen Warteschlange planen, werden sie seriell ausgeführt. Es unterscheidet sich nicht von der Planung der Blöcke ohne Gruppen.
Jörn Eyrich

1
Gilt dies auch für die Ausführung von Webdienstbeiträgen?
SleepNot

Bemerken Sie, dass die Zeit nicht der in Ihrem Block eingestellten Schlafzeit entspricht? warum sollte es so sein?
Damon Yuan

2
In ARC entfernen Sie einfach dispatch_release (Gruppe);
Loretoparisi

272

Wenn Sie die Antwort von Jörn Eyrich erweitern (seine Antwort verbessern, wenn Sie diese beantworten), dispatch_asynckönnen Sie die GCD-Gruppen mit dispatch_group_enterund dispatch_group_leavedirekt verwenden , wenn Sie nicht die Kontrolle über die Aufrufe Ihrer Blöcke haben, wie dies bei asynchronen Abschlussblöcken der Fall sein kann .

In diesem Beispiel tun computeInBackgroundwir so, als könnten wir nichts ändern (stellen Sie sich vor, es handelt sich um einen Delegaten-Rückruf, einen NSURLConnection CompletionHandler oder was auch immer), und daher haben wir keinen Zugriff auf die Versandaufrufe.

// create a group
dispatch_group_t group = dispatch_group_create();

// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);     // pair 1 enter
[self computeInBackground:1 completion:^{
    NSLog(@"1 done");
    dispatch_group_leave(group); // pair 1 leave
}];

// again... (and again...)
dispatch_group_enter(group);     // pair 2 enter
[self computeInBackground:2 completion:^{
    NSLog(@"2 done");
    dispatch_group_leave(group); // pair 2 leave
}];

// Next, setup the code to execute after all the paired enter/leave calls.
//
// Option 1: Get a notification on a block that will be scheduled on the specified queue:
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"finally!");
});

// Option 2: Block an wait for the calls to complete in code already running
// (as cbartel points out, be careful with running this on the main/UI queue!):
//
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // blocks current thread
// NSLog(@"finally!");

In diesem Beispiel wird computeInBackground: vervollständigung: wie folgt implementiert:

- (void)computeInBackground:(int)no completion:(void (^)(void))block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"%d starting", no);
        sleep(no*2);
        block();
    });
}

Ausgabe (mit Zeitstempeln aus einem Lauf):

12:57:02.574  2 starting
12:57:02.574  1 starting
12:57:04.590  1 done
12:57:06.590  2 done
12:57:06.591  finally!

1
@ ɲeuroburɳ Der obige Code wartet auf den Haupt-Thread. Ich glaube, dies wird den Hauptthread blockieren und dazu führen, dass die Benutzeroberfläche nicht mehr reagiert, bis die gesamte Gruppe vollständig ist. Ich empfehle, die Wartezeit in einen Hintergrund-Thread zu verschieben. Zum Beispiel dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0)
cbartel

2
@cbartel, guter Fang! Ich habe den Beispielcode aktualisiert, um Ihren Kommentar wiederzugeben. Oft muss sich der Rückruf in der Hauptwarteschlange befinden - in diesem Fall dispatch_queue_notifyist dies wahrscheinlich besser (es sei denn, die Blockierungszeit ist garantiert kurz).
ɲeuroburɳ

Wo kann ich die Gruppe freigeben (dh dispatch_release (Gruppe))? Ich bin mir nicht sicher, ob die Freigabe in dispatch_group_notify sicher ist. Da dies jedoch der Code ist, der nach Abschluss der Gruppe ausgeführt wird, bin ich mir nicht sicher, wo er veröffentlicht werden soll.
GingerBreadMane

Wenn Sie ARC verwenden, müssen Sie dispatch_release nicht aufrufen: stackoverflow.com/questions/8618632/…
ɲeuroburɳ

3
Netter Beitrag, der das weiter erklärt: commandshift.co.uk/blog/2014/03/19/…
Rizon

96

Mit Swift 5.1 bietet Grand Central Dispatch viele Möglichkeiten, um Ihr Problem zu lösen. Je nach Bedarf können Sie eines der sieben Muster auswählen , die in den folgenden Spielplatzausschnitten gezeigt werden.


# 1. Verwenden von DispatchGroup, DispatchGroup's notify(qos:flags:queue:execute:)und DispatchQueue' sasync(group:qos:flags:execute:)

Im Apple Developer Concurrency Programming Guide wird Folgendes beschriebenDispatchGroup :

Dispatch-Gruppen sind eine Möglichkeit, einen Thread zu blockieren, bis eine oder mehrere Aufgaben ausgeführt sind. Sie können dieses Verhalten an Orten verwenden, an denen Sie erst dann Fortschritte erzielen können, wenn alle angegebenen Aufgaben abgeschlossen sind. Nachdem Sie beispielsweise mehrere Aufgaben zum Berechnen einiger Daten gesendet haben, können Sie eine Gruppe verwenden, um auf diese Aufgaben zu warten und die Ergebnisse dann zu verarbeiten, wenn sie erledigt sind.

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

queue.async(group: group) {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async(group: group) {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

group.notify(queue: queue) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 2. Verwenden von DispatchGroup, DispatchGroup's wait(), DispatchGroup' s enter()und DispatchGroup'sleave()

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let group = DispatchGroup()

group.enter()
queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    group.leave()
}

group.enter()
queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    group.leave()
}

queue.async {
    group.wait()
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

Beachten Sie, dass Sie auch DispatchGroup wait()mit DispatchQueue async(group:qos:flags:execute:)oder mischen DispatchGroup enter()und DispatchGroup leave()mit mischen können DispatchGroup notify(qos:flags:queue:execute:).


#3. Verwenden von und 'sDispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Grand Central Dispatch Tutorial für Swift 4: Teil 1/2 Artikel von Raywenderlich.com enthält eine Definition für Barrieren :

Versandbarrieren sind eine Gruppe von Funktionen, die bei der Arbeit mit gleichzeitigen Warteschlangen als serieller Engpass fungieren. Wenn Sie eine DispatchWorkIteman eine Versandwarteschlange senden , können Sie Flags setzen, um anzugeben, dass dies das einzige Element sein soll, das für diese bestimmte Zeit in der angegebenen Warteschlange ausgeführt wird. Dies bedeutet, dass alle Elemente, die vor der Versandbarriere an die Warteschlange gesendet wurden, abgeschlossen sein müssen, bevor die DispatchWorkItemAusführung ausgeführt wird.

Verwendung:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

queue.async(flags: .barrier) {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 4. Verwenden von DispatchWorkItem, Dispatch​Work​Item​Flags's barrierund DispatchQueue' sasync(execute:)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .barrier) {
    print("#3 finished")
}

queue.async(execute: dispatchWorkItem)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 5. Verwenden von DispatchSemaphore, DispatchSemaphore's wait()und DispatchSemaphore' ssignal()

Soroush Khanlou schrieb die folgenden Zeilen im Blog-Beitrag des GCD-Handbuchs :

Mit einem Semaphor können wir einen Thread für eine beliebige Zeitspanne blockieren, bis ein Signal von einem anderen Thread gesendet wird. Semaphoren sind wie der Rest der GCD threadsicher und können von überall aus ausgelöst werden. Semaphore können verwendet werden, wenn es eine asynchrone API gibt, die Sie synchronisieren müssen, die Sie jedoch nicht ändern können.

Die Apple Developer API-Referenz enthält auch die folgende Diskussion für den DispatchSemaphore init(value:​)Initialisierer:

Das Übergeben von Null für den Wert ist nützlich, wenn zwei Threads den Abschluss eines bestimmten Ereignisses abgleichen müssen. Das Übergeben eines Werts größer als Null ist nützlich, um einen endlichen Ressourcenpool zu verwalten, bei dem die Poolgröße dem Wert entspricht.

Verwendung:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let queue = DispatchQueue(label: "com.company.app.queue", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: 0)

queue.async {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
    semaphore.signal()
}

queue.async {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
    semaphore.signal()
}

queue.async {
    semaphore.wait()
    semaphore.wait()    
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 */

# 6. Verwenden von OperationQueueund Operation'saddDependency(_:)

In der Apple Developer API-Referenz heißt es über Operation​Queue:

Operationswarteschlangen verwenden die libdispatchBibliothek (auch als Grand Central Dispatch bezeichnet), um die Ausführung ihrer Operationen zu initiieren.

Verwendung:

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let operationQueue = OperationQueue()

let blockOne = BlockOperation {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

let blockTwo = BlockOperation {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

let blockThree = BlockOperation {
    print("#3 finished")
}

blockThree.addDependency(blockOne)
blockThree.addDependency(blockTwo)

operationQueue.addOperations([blockThree, blockTwo, blockOne], waitUntilFinished: false)

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */

# 7. Verwenden von OperationQueueund OperationQueue's addBarrierBlock(_:)(erfordert iOS 13)

import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

let operationQueue = OperationQueue()

let blockOne = BlockOperation {
    print("#1 started")
    Thread.sleep(forTimeInterval: 5)
    print("#1 finished")
}

let blockTwo = BlockOperation {
    print("#2 started")
    Thread.sleep(forTimeInterval: 2)
    print("#2 finished")
}

operationQueue.addOperations([blockTwo, blockOne], waitUntilFinished: false)
operationQueue.addBarrierBlock {
    print("#3 finished")
}

/*
 prints:
 #1 started
 #2 started
 #2 finished
 #1 finished
 #3 finished
 or
 #2 started
 #1 started
 #2 finished
 #1 finished
 #3 finished
 */

Gibt es eine Lösung für asynchrone Aufrufe ohne Verwendung von group.enter () und group.leave () für jeden (und ohne Semaphoren)? Wenn ich auf eine asynchrone Anforderung an einen Server warten muss, warten Sie danach auf eine zweite asynchrone Anforderung und so weiter. Ich habe diesen Artikel unter avanderlee.com/swift/asynchronous-operations gelesen, sehe aber keine einfache Verwendung im Vergleich zu BlockOperation
Woof,

58

Eine weitere GCD-Alternative ist eine Barriere:

dispatch_queue_t queue = dispatch_queue_create("com.company.app.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{ 
    NSLog(@"start one!\n");  
    sleep(4);  
    NSLog(@"end one!\n");
});

dispatch_async(queue, ^{  
    NSLog(@"start two!\n");  
    sleep(2);  
    NSLog(@"end two!\n"); 
});

dispatch_barrier_async(queue, ^{  
    NSLog(@"Hi, I'm the final block!\n");  
});

Erstellen Sie einfach eine gleichzeitige Warteschlange, versenden Sie Ihre beiden Blöcke und versenden Sie dann den letzten Block mit Barriere, sodass Sie warten müssen, bis die beiden anderen fertig sind.


Gibt es ein Problem, wenn ich nicht geschlafen habe (4)?
Himanth

Nein, damit gibt es natürlich kein Problem. In der Tat wollen Sie praktisch nie sleep()! Ich habe diese sleep()Aufrufe nur aus pädagogischen Gründen hinzugefügt , damit die Blöcke lange genug laufen, damit Sie sehen können, dass sie gleichzeitig laufen. In diesem trivialen Beispiel können sleep()diese beiden Blöcke in Abwesenheit von so schnell ausgeführt werden, dass der versendete Block möglicherweise gestartet und beendet wird, bevor Sie die Möglichkeit haben, die gleichzeitige Ausführung empirisch zu beobachten. Aber nicht sleep()in Ihrem eigenen Code.
Rob

39

Ich weiß, dass Sie nach GCD gefragt haben, aber wenn Sie wollten, können Sie NSOperationQueuediese Art von Dingen auch sehr anmutig behandeln, z.

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 3");
}];

NSOperation *operation;

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 1");
    sleep(7);
    NSLog(@"Finishing 1");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Starting 2");
    sleep(5);
    NSLog(@"Finishing 2");
}];

[completionOperation addDependency:operation];
[queue addOperation:operation];

[queue addOperation:completionOperation];

3
Dies ist in Ordnung, wenn der Code in Ihrer NSBlockOperation synchron ist. Aber was ist, wenn dies nicht der Fall ist und Sie den Abschluss auslösen möchten, wenn Ihre asynchrone Operation abgeschlossen ist?
Greg Maletic

3
@GregMaletic In diesem Fall erstelle ich eine NSOperationUnterklasse, die gleichzeitig ausgeführt wird, isFinishedwenn der asynchrone Prozess abgeschlossen ist. Dann funktionieren die Abhängigkeiten einwandfrei.
Rob


1
@GregMaletic Ja, das können Sie auch verwenden (solange dispatch_semaphore_waites nicht in der Hauptwarteschlange stattfindet und Ihre Signale und Wartezeiten ausgeglichen sind). Solange Sie die Hauptwarteschlange nicht blockieren, ist ein Semaphor-Ansatz in Ordnung, wenn Sie nicht die Flexibilität von Vorgängen benötigen (z. B. die Möglichkeit, sie abzubrechen, den Grad der Parallelität zu steuern usw.).
Rob

1
@ Reza.Ab - Wenn Sie die erste Aufgabe vor dem Start der zweiten Aufgabe abschließen müssen, fügen Sie eine Abhängigkeit zwischen diesen Aufgaben hinzu. Oder wenn die Warteschlange immer nur eine Aufgabe zu einem Zeitpunkt durchgeführt wird , macht es zu einem seriellen Warteschlange , indem Sie maxConcurrentOperationCountauf 1. Sie können auch die Priorität von Vorgängen festlegen, sowohl die qualityOfServiceals auch queuePriority, aber diese haben einen weitaus subtileren Einfluss auf die Aufgabenpriorität als die Abhängigkeiten und / oder der Grad der Parallelität der Warteschlangen.
Rob

4

Die obigen Antworten sind alle cool, aber sie haben alle eine Sache übersehen. group führt Aufgaben (Blöcke) in dem Thread aus, in den sie eingegeben wurden, wenn Sie dispatch_group_enter/ verwenden dispatch_group_leave.

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      dispatch_async(demoQueue, ^{
        dispatch_group_t demoGroup = dispatch_group_create();
        for(int i = 0; i < 10; i++) {
          dispatch_group_enter(demoGroup);
          [self testMethod:i
                     block:^{
                       dispatch_group_leave(demoGroup);
                     }];
        }

        dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
          NSLog(@"All group tasks are done!");
        });
      });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

Dies wird in der erstellten gleichzeitigen Warteschlange ausgeführt demoQueue. Wenn ich keine Warteschlange erstelle, läuft sie im Hauptthread .

- (IBAction)buttonAction:(id)sender {
    dispatch_group_t demoGroup = dispatch_group_create();
    for(int i = 0; i < 10; i++) {
      dispatch_group_enter(demoGroup);
      [self testMethod:i
                 block:^{
                   dispatch_group_leave(demoGroup);
                 }];
    }

    dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
      NSLog(@"All group tasks are done!");
    });
    }

    - (void)testMethod:(NSInteger)index block:(void(^)(void))completeBlock {
      NSLog(@"Group task started...%ld", index);
      NSLog(@"Current thread is %@ thread", [NSThread isMainThread] ? @"main" : @"not main");
      [NSThread sleepForTimeInterval:1.f];

      if(completeBlock) {
        completeBlock();
      }
    }

und es gibt eine dritte Möglichkeit, Aufgaben in einem anderen Thread auszuführen:

- (IBAction)buttonAction:(id)sender {
      dispatch_queue_t demoQueue = dispatch_queue_create("com.demo.group", DISPATCH_QUEUE_CONCURRENT);
      //  dispatch_async(demoQueue, ^{
      __weak ViewController* weakSelf = self;
      dispatch_group_t demoGroup = dispatch_group_create();
      for(int i = 0; i < 10; i++) {
        dispatch_group_enter(demoGroup);
        dispatch_async(demoQueue, ^{
          [weakSelf testMethod:i
                         block:^{
                           dispatch_group_leave(demoGroup);
                         }];
        });
      }

      dispatch_group_notify(demoGroup, dispatch_get_main_queue(), ^{
        NSLog(@"All group tasks are done!");
      });
      //  });
    }

Natürlich können Sie, wie bereits erwähnt, verwenden dispatch_group_async, um das zu bekommen, was Sie wollen.


3

Die erste Antwort ist im Wesentlichen richtig. Wenn Sie jedoch den einfachsten Weg suchen, um das gewünschte Ergebnis zu erzielen, finden Sie hier ein eigenständiges Codebeispiel, das zeigt, wie dies mit einem Semaphor gemacht wird (wie auch Versandgruppen hinter den Kulissen arbeiten, JFYI). ::

#include <dispatch/dispatch.h>
#include <stdio.h>

main()
{
        dispatch_queue_t myQ = dispatch_queue_create("my.conQ", DISPATCH_QUEUE_CONCURRENT);
        dispatch_semaphore_t mySem = dispatch_semaphore_create(0);

        dispatch_async(myQ, ^{ printf("Hi I'm block one!\n"); sleep(2); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ printf("Hi I'm block two!\n"); sleep(4); dispatch_semaphore_signal(mySem);});
        dispatch_async(myQ, ^{ dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER); printf("Hi, I'm the final block!\n"); });
        dispatch_main();
}

7
Zwei Beobachtungen: 1. Sie vermissen a dispatch_semaphore_wait. Sie haben zwei Signale, also brauchen Sie zwei Wartezeiten. Wie es ist, beginnt Ihr "Abschluss" -Block, sobald der erste Block das Semaphor signalisiert, aber bevor der andere Block beendet ist. 2. Da dies eine iOS-Frage war, würde ich von der Verwendung von abraten dispatch_main.
Rob

1
Ich stimme Rob zu. Dies ist keine gültige Lösung. Das dispatch_semaphore_waitwird entsperrt, sobald eine der dispatch_semaphore_signalMethoden aufgerufen wird. Der Grund, warum dies zu funktionieren scheint, ist, dass die printffor-Blöcke 'eins' und 'zwei' sofort auftreten und die printffür 'endlich' nach einer Wartezeit auftreten - also nachdem der Block 2 Sekunden lang geschlafen hat. Wenn Sie den Ausdruck nach den sleepAufrufen einfügen , erhalten Sie die Ausgabe für 'eins', dann 2 Sekunden später für 'endlich' und 2 Sekunden später für 'zwei'.
ɲeuroburɳ

1

Akzeptierte Antwort in Kürze:

let group = DispatchGroup()

group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block1
    print("Block1")
    Thread.sleep(forTimeInterval: 5.0)
    print("Block1 End")
})


group.async(group: DispatchQueue.global(qos: .default), execute: {
    // block2
    print("Block2")
    Thread.sleep(forTimeInterval: 8.0)
    print("Block2 End")
})

dispatch_group_notify(group, DispatchQueue.global(qos: .default), {
    // block3
    print("Block3")
})

// only for non-ARC projects, handled automatically in ARC-enabled projects.
dispatch_release(group)

0

Swift 4.2 Beispiel:

let group = DispatchGroup.group(count: 2)
group.notify(queue: DispatchQueue.main) {
     self.renderingLine = false
     // all groups are done
}
DispatchQueue.main.async {
    self.renderTargetNode(floorPosition: targetPosition, animated: closedContour) {
        group.leave()
        // first done
    }
    self.renderCenterLine(position: targetPosition, animated: closedContour) {
        group.leave()
        // second done
    }
 }

group.leave()verursachte Absturz
Ben

-3

Um nicht zu sagen, dass andere Antworten unter bestimmten Umständen nicht gut sind, aber dies ist ein Ausschnitt, den ich immer von Google benutze:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


    if (signInDoneSel) {
        [self performSelector:signInDoneSel];
    }

}
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.