wake-up-neo.com

Warten, bis zwei asynchrone Blöcke ausgeführt werden, bevor ein weiterer Block gestartet wird

Wenn Sie GCD verwenden, möchten wir warten, bis zwei async-Blöcke ausgeführt und fertig sind, bevor Sie mit den nächsten Ausführungsschritten fortfahren. Was ist der beste Weg dies 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
});
174
tom

Verwenden Sie Dispatch-Gruppen: Siehe hier für ein Beispiel: "Warten auf Gruppen von Warteschlangenaufgaben" im Kapitel "Dispatch-Warteschlangen" des Apple Concurrent-Programmierhandbuchs für iOS Developer Library

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 so produzieren:

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
284
Jörn Eyrich

Wenn Sie die Antwort von Jörn Eyrich erweitern (seine Antwort wird positiv bestätigt), können Sie, wenn Sie die dispatch_async-Aufrufe für Ihre Blöcke nicht steuern, wie es bei asynchronen Abschlussblöcken der Fall ist, die GCD-Gruppen mit dispatch_group_enter und dispatch_group_leave verwenden. direkt.

In diesem Beispiel geben wir vor, computeInBackground ist etwas, das wir nicht ändern können (stellen Sie sich vor, es handelt sich um einen Delegaten-Callback, um NSURLConnection-CompletionHandler oder was auch immer), und daher haben wir keinen Zugriff auf die Dispatch-Aufrufe.

// 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: completion: implementiert als:

- (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 von 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!
260
ɲeuroburɳ

Eine andere 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, schicken Sie Ihre zwei Blöcke ab und schicken Sie den letzten Block mit Barriere ab. Dadurch wird gewartet, bis die beiden anderen beendet sind. 

56
Rob

Mit Swift 3 bietet Grand Central Dispatch viele Möglichkeiten, Ihr Problem zu lösen. Je nach Ihren Bedürfnissen können Sie eines der six-Muster auswählen, die in den folgenden Playground-Ausschnitten gezeigt werden.


# 1. Verwenden Sie die Methoden DispatchGroup , DispatchGroupnotify(qos:flags:queue:execute:) und DispatchQueueasync(group:qos:flags:execute:)

Das Apple Developer Concurrency-Programmierhandbuch besagt DispatchGroup :

Mithilfe von Dispatch-Gruppen kann ein Thread blockiert werden, bis die Ausführung einer oder mehrerer Aufgaben abgeschlossen ist. Sie können dieses Verhalten an Orten verwenden, an denen Sie keine Fortschritte erzielen können, bis alle angegebenen Aufgaben abgeschlossen sind. Nachdem Sie beispielsweise mehrere Aufgaben zur Berechnung einiger Daten ausgeführt haben, könnten Sie eine Gruppe verwenden, um auf diese Aufgaben zu warten und die Ergebnisse dann zu verarbeiten, wenn sie abgeschlossen 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 der Methoden DispatchGroup , DispatchGroupwait() , DispatchGroupenter() und DispatchGroupleave()

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 DispatchGroupwait() mit DispatchQueueasync(group:qos:flags:execute:) mischen oder DispatchGroupenter() und DispatchGroupleave() mit DispatchGroupnotify(qos:flags:queue:execute:) mischen können.


#3. Verwenden Sie Dispatch​Work​Item​Flagsbarrier eigenschaft und DispatchQueueDispatchQueueasync(group:qos:flags:execute:) Methode

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

Dispatchbarrieren sind eine Gruppe von Funktionen, die beim Arbeiten mit gleichzeitigen Warteschlangen als Flaschenhals mit seriellem Stil fungieren. [...] Wenn Sie eine DispatchWorkItem an eine Dispatch-Warteschlange übergeben, können Sie Flags setzen, um anzugeben, dass es sich um das einzige Element handelt, das zu diesem Zeitpunkt in der angegebenen Warteschlange ausgeführt wird. Dies bedeutet, dass alle Elemente, die vor der Versandbeschränkung an die Warteschlange übermittelt wurden, abgeschlossen sein müssen, bevor die Variable DispatchWorkItem ausgeführt wird.

Verwendungszweck:

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 Sie DispatchWorkItem , Dispatch​Work​Item​Flagsbarrier eigenschaft und DispatchQueueasync(execute:) methode

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 der Methoden DispatchSemaphore , DispatchSemaphorewait() und DispatchSemaphoresignal()

Soroush Khanlou schrieb die folgenden Zeilen in Das GCD-Handbuch Blogpost:

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

In der Apple Developer API-Referenz finden Sie auch die folgenden Informationen zum Initialisierer DispatchSemaphoreinit(value:​) :

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 hilfreich für die Verwaltung eines begrenzten Ressourcenpools, bei dem die Poolgröße dem Wert entspricht.

Verwendungszweck:

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 OperationQueue und BlockOperation

In der Apple Developer API Reference wird Operation​Queue angegeben:

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

Verwendungszweck:

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
 */
48
Imanou Petit

Ich weiß, dass Sie nach GCD gefragt haben, aber wenn Sie möchten, NSOperationQueue behandelt diese Art von Material auch wirklich zierlich, 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];
39
Rob

Die obigen Antworten sind alle cool, aber alle haben eines übersehen. Gruppe führt Aufgaben (Blöcke) im Thread aus, in den sie bei Verwendung von dispatch_group_enter/dispatch_group_leave eingegeben wurde.

- (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 demoQueue ausgeführt. Wenn ich keine Warteschlange erstelle, läuft sie in main thread .

- (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!");
      });
      //  });
    }

Wie bereits erwähnt, können Sie dispatch_group_async verwenden, um das zu bekommen, was Sie möchten.

3
Bruce Lee

Die erste Antwort ist im Wesentlichen richtig. Wenn Sie jedoch auf einfachste Weise das gewünschte Ergebnis erzielen möchten, finden Sie hier ein eigenständiges Codebeispiel, das die Verwendung eines Semaphors veranschaulicht. :

#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();
}
3
jkh

Akzeptierte Antwort in Swift:

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)
1
Outsider

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
    }
 }
0
atereshkov