Oczekiwanie na wykonanie dwóch bloków asynchronicznych przed rozpoczęciem kolejnego bloku

192

Korzystając z GCD, chcemy przejść do wykonania i wykonania dwóch bloków asynchronicznych, zanim przejdziemy do kolejnych kroków wykonania. Jak najlepiej to zrobić?

Wypróbowaliśmy następujące, ale wydaje się, że to nie działa:

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
});
Tomek
źródło
Zobacz moją odpowiedź dla Swift 5, która oferuje do sześciu różnych sposobów rozwiązania twojego problemu.
Imanou Petit

Odpowiedzi:

301

Użyj grup wysyłkowych: zobacz tutaj przykład „Oczekiwanie na grupy zadań w kolejce” w rozdziale „Kolejki wysyłkowe” w Przewodniku programowania współbieżności w bibliotece programistów iOS firmy Apple

Twój przykład może wyglądać mniej więcej tak:

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);

i może produkować takie dane wyjściowe:

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
Jörn Eyrich
źródło
3
Chłodny. czy zadania / bloki asynchroniczne, po powiązaniu z grupą, będą wykonywane sekwencyjnie lub jednocześnie? To znaczy, załóżmy, że blok 1 i blok 2 są teraz powiązane z grupą, czy blok 2 zaczeka, aż blok 1 zostanie zakończony, zanim będzie mógł rozpocząć wykonywanie?
tom
9
To zależy od Ciebie. dispatch_group_asyncjest jak dispatch_asyncz dodanym parametrem grupy. Jeśli więc użyjesz różnych kolejek dla bloku 1 i bloku 2 lub zaplanujesz je w tej samej współbieżnej kolejce, mogą one działać jednocześnie; jeśli zaplanujesz je w tej samej kolejce szeregowej, będą one działać szeregowo. Nie różni się niczym od planowania bloków bez grup.
Jörn Eyrich,
1
Czy dotyczy to również wykonywania postów serwisu internetowego?
SleepNot
Czy zauważysz, że czas nie jest równy czasowi snu ustawionemu w bloku? dlaczego tak by było
Damon Yuan,
2
W ARC wystarczy usunąć dispatch_release (group);
loretoparisi
272

Rozwijając odpowiedź Jörna Eyricha (głosuj na jego odpowiedź, jeśli ją zagłosujesz), jeśli nie masz kontroli nad dispatch_asyncwywołaniami bloków, tak jak w przypadku bloków asynchronicznych, możesz używać grup GCD bezpośrednio dispatch_group_enteri dispatch_group_leavebezpośrednio.

W tym przykładzie udajemy, że computeInBackgroundnie możemy tego zmienić (wyobraź sobie, że jest to wywołanie zwrotne dla delegata, moduł obsługi zakończenia NSURLConnection, lub cokolwiek innego), a zatem nie mamy dostępu do wywołań wysyłki.

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

W tym przykładzie computeInBackground: complete: jest zaimplementowany jako:

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

Dane wyjściowe (ze znacznikami czasu z przebiegu):

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!
ɲeuroburɳ
źródło
1
@ ɲeuroburɳ Powyższy kod czeka na główny wątek. Wierzę, że to zablokuje główny wątek i spowoduje, że interfejs użytkownika nie będzie odpowiadał, dopóki cała grupa nie będzie kompletna. Polecam przeniesienie oczekiwania do wątku w tle. Na przykład dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0)
cbartel
2
@cartart, dobry połów! Zaktualizowałem przykładowy kod, aby odzwierciedlić twój komentarz. Wiele razy potrzebujesz wywołania zwrotnego, aby znaleźć się w głównej kolejce - w takim przypadku jednak dispatch_queue_notifyprawdopodobnie jest to lepsze (chyba że czas blokowania jest gwarantowany na krótki).
ɲeuroburɳ
Gdzie mogę zwolnić grupę (tj. Dispatch_release (group))? Nie jestem pewien, czy można go bezpiecznie wydać w dispatch_group_notify. Ale ponieważ taki kod jest uruchamiany po zakończeniu grupy, nie jestem pewien, gdzie go zwolnić.
GingerBreadMane
Jeśli używasz ARC, nie musisz dzwonić do
wydania
3
Nicea po co dodatkowo wyjaśnia, że: commandshift.co.uk/blog/2014/03/19/...
Rizon
97

Dzięki Swift 5.1 Grand Central Dispatch oferuje wiele sposobów rozwiązania problemu. W zależności od potrzeb możesz wybrać jeden z siedmiu wzorów pokazanych w poniższych fragmentach placu zabaw.


# 1. Korzystanie DispatchGroup, DispatchGroup's notify(qos:flags:queue:execute:)i DispatchQueue' sasync(group:qos:flags:execute:)

Przewodnik programisty współbieżności dla programistów Apple mówi oDispatchGroup :

Grupy wysyłania są sposobem blokowania wątku, dopóki jedno lub więcej zadań nie zakończy się. Możesz użyć tego zachowania w miejscach, w których nie można robić postępów, dopóki wszystkie określone zadania nie zostaną ukończone. Na przykład po wysłaniu kilku zadań do obliczenia niektórych danych możesz użyć grupy, aby poczekać na te zadania, a następnie przetworzyć wyniki po ich zakończeniu.

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. Korzystanie DispatchGroup, DispatchGroup's wait(), DispatchGroup' s enter()i 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
 */

Pamiętaj, że możesz także miksować DispatchGroup wait()z DispatchQueue async(group:qos:flags:execute:)lub miksować z DispatchGroup enter()i .DispatchGroup leave()DispatchGroup notify(qos:flags:queue:execute:)


# 3. Używanie i „sDispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Samouczek Grand Central Dispatch dla Swift 4: część 1/2 artykułu z Raywenderlich.com zawiera definicję barier :

Bariery wysyłki to grupa funkcji działających jako wąskie gardło w stylu szeregowym podczas pracy z równoległymi kolejkami. Kiedy przesyłasz a DispatchWorkItemdo kolejki wysyłkowej, możesz ustawić flagi wskazujące, że powinien to być jedyny element wykonywany w określonej kolejce dla tego określonego czasu. Oznacza to, że wszystkie elementy przesłane do kolejki przed barierą wysyłkową muszą zostać ukończone przed DispatchWorkItemwykonaniem.

Stosowanie:

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. Korzystanie DispatchWorkItem, Dispatch​Work​Item​Flags's barrieri 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. Korzystanie DispatchSemaphore, DispatchSemaphore's wait()i DispatchSemaphore' ssignal()

Soroush Khanlou napisał następujące wiersze w poście na blogu The GCD Handbook :

Za pomocą semafora możemy blokować wątek na dowolny czas, dopóki nie zostanie wysłany sygnał z innego wątku. Semafory, podobnie jak reszta GCD, są bezpieczne dla wątków i można je uruchamiać z dowolnego miejsca. Semaforów można używać, gdy istnieje asynchroniczny interfejs API, który należy synchronizować, ale nie można go modyfikować.

Dokumentacja interfejsu API dla programistów Apple zawiera także następujące omówienie DispatchSemaphore init(value:​)inicjatora:

Przekazywanie wartości zero jest przydatne, gdy dwa wątki muszą uzgodnić zakończenie określonego zdarzenia. Przekazywanie wartości większej niż zero jest przydatne do zarządzania skończoną pulą zasobów, w której wielkość puli jest równa wartości.

Stosowanie:

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. Używanie OperationQueuei Operation„saddDependency(_:)

Dokumentacja interfejsu API Apple Developer zawiera następujące informacje Operation​Queue:

Kolejki operacji używają libdispatchbiblioteki (znanej również jako Grand Central Dispatch) do inicjowania wykonywania swoich operacji.

Stosowanie:

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. Używanie OperationQueuei OperationQueue„S addBarrierBlock(_:)(wymaga 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
 */
Imanou Petit
źródło
Czy istnieje rozwiązanie dla wywołań asynchronicznych bez użycia group.enter () i group.leave () dla każdego (i bez semaforów)? Jak jeśli muszę czekać na żądanie asynchroniczne do serwera, to potem poczekaj na drugie żądanie asynchroniczne i tak dalej. Przeczytałem ten artykuł avanderlee.com/swift/asynchronous-operations, ale nie widzę prostego użycia tego w porównaniu do BlockOperation
Woof
58

Inną alternatywą dla GCD jest bariera:

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

Po prostu utwórz współbieżną kolejkę, wyślij dwa bloki, a następnie wyślij ostatni blok z barierą, co sprawi, że będzie czekać na zakończenie pozostałych dwóch.

Obrabować
źródło
Czy jest jakiś problem, gdybym nie używał snu (4);
Himanth
Nie, oczywiście nie ma z tym problemu. W rzeczywistości praktycznie nigdy nie chcesz sleep()! Dodałem te sleep()wezwania tylko ze względów pedagogicznych, aby bloki działały wystarczająco długo, aby można było zobaczyć, że biegną one jednocześnie. W tym trywialnym przykładzie, przy braku sleep()tych dwóch bloków, mogą one działać tak szybko, że wysłany blok może rozpocząć się i zakończyć, zanim będziesz miał szansę empirycznie obserwować równoczesne wykonanie. Ale nie sleep()wpisuj własnego kodu.
Rob
39

Wiem, że pytałeś o GCD, ale jeśli chcesz, NSOperationQueueradzi sobie z tego rodzaju rzeczami naprawdę wdzięcznie, np .:

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];
Obrabować
źródło
3
Jest to w porządku, gdy kod w twojej NSBlockOperation jest synchroniczny. Ale co jeśli nie jest, a chcesz uruchomić zakończenie po zakończeniu operacji asynchronicznej?
Greg Maletic
3
@GregMaletic W takim przypadku tworzę NSOperationpodklasę, która jest współbieżna i ustawiana isFinishedpo zakończeniu procesu asynchronicznego. Wtedy zależności działają dobrze.
Rob
1
@GregMaletic Tak, możesz tego również użyć (o ile dispatch_semaphore_waitnie odbywa się to w głównej kolejce i dopóki twoje sygnały i oczekiwania są zrównoważone). Tak długo, jak nie blokujesz głównej kolejki, podejście semaforowe jest w porządku, jeśli nie potrzebujesz elastyczności operacji (np. Mając możliwość anulowania ich, kontrolę stopnia współbieżności itp.).
Rob
1
@ Reza.Ab - Jeśli potrzebujesz zadania pierwszego do ukończenia przed rozpoczęciem zadania drugiego, dodaj zależność między tymi zadaniami. Lub jeśli kolejka zawsze wykonuje tylko jedno zadanie na raz, ustaw ją jako kolejkę szeregową, ustawiając maxConcurrentOperationCountna 1. Możesz także ustawić priorytet operacji, zarówno, jak qualityOfServicei queuePriority, ale mają one znacznie bardziej subtelny wpływ na priorytet zadania niż zależności i / lub stopień współbieżności kolejki.
Rob
4

Wszystkie powyższe odpowiedzi są fajne, ale wszystkie przeoczyły jedną rzecz. grupa wykonuje zadania (bloki) w wątku, w którym wszedł, gdy używasz dispatch_group_enter/ 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();
      }
    }

działa to w utworzonej współbieżnej kolejce demoQueue. Jeśli nie utworzę żadnej kolejki, działa ona w głównym wątku .

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

i istnieje trzeci sposób na wykonanie zadań w innym wątku:

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

Oczywiście, jak wspomniano, możesz użyć, dispatch_group_asyncaby uzyskać to, czego chcesz.

Bruce Lee
źródło
3

Pierwsza odpowiedź jest zasadniczo poprawna, ale jeśli chcesz najprostszego sposobu osiągnięcia pożądanego rezultatu, oto samodzielny przykład kodu pokazujący, jak to zrobić za pomocą semafora (tak też działają grupy wysyłające za kulisami, 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();
}
jkh
źródło
7
Dwie spostrzeżenia: 1. Brakuje a dispatch_semaphore_wait. Masz dwa sygnały, więc potrzebujesz dwóch czekań. W tej chwili blok „uzupełniający” rozpocznie się, gdy pierwszy blok zasygnalizuje semafor, ale zanim zakończy się drugi blok; 2. Biorąc pod uwagę, że to pytanie dotyczyło systemu iOS, odradzam korzystanie z niego dispatch_main.
Rob
1
Zgadzam się z Robem. To nie jest poprawne rozwiązanie. dispatch_semaphore_waitOdblokuje jak tylko jedną z dispatch_semaphore_signalmetod są nazywane. Może się wydawać, że to działa, ponieważ printfdla bloków „jeden” i „dwa” występują natychmiast, a printfdla „w końcu” następuje po odczekaniu - a więc po bloku spał przez 2 sekundy. Jeśli umieścisz printf po sleepwywołaniach, otrzymasz wynik dla „jeden”, następnie 2 sekundy później dla „w końcu”, a następnie 2 sekundy później dla „dwóch”.
ɲeuroburɳ
1

Szybka odpowiedź zaakceptowana:

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)

źródło
0

Przykład Swift 4.2:

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
    }
 }
atereshkov
źródło
group.leave()spowodował awarię
Ben
-3

Nie wspominając, że inne odpowiedzi nie są świetne w pewnych okolicznościach, ale jest to jeden fragment, którego zawsze używam od Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


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

}
ChuckKelly
źródło