Başka bir bloğa başlamadan önce iki eşzamansız blok yürütülmesini beklemek


192

GCD kullanırken, sonraki yürütme adımlarına geçmeden önce iki eşzamansız blok yürütülene ve tamamlanana kadar beklemek istiyoruz. Bunu yapmanın en iyi yolu nedir?

Aşağıdakileri denedik, ancak işe yaramıyor gibi görünüyor:

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

Sorununuzu çözmek için altı farklı yol sunan Swift 5 için cevabımı görün .
Imanou Petit

Yanıtlar:


301

Dağıtım gruplarını kullanın: Apple'ın iOS Geliştirici Kitaplığı'nın Eşzamanlılık Programlama Kılavuzu'nun "Dağıtım Kuyrukları" bölümündeki "Kuyruktaki Görev Gruplarını Bekleme" adlı bir örnek için buraya bakın

Örneğiniz şöyle görünebilir:

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

ve bunun gibi çıktılar üretebilir:

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
Güzel. grupla ilişkilendirildikten sonra zaman uyumsuz görevler / bloklar sırayla veya eşzamanlı olarak yürütülecek mi? Yani, block1 ve block2'nin şimdi bir grupla ilişkili olduğunu varsayalım, block2 yürütülmeye başlamadan önce block2 bitene kadar bekleyecek mi?
tom

9
Bu size kalmış. dispatch_group_asynctıpkı dispatch_asyncbir grup parametresi eklendiğinde olduğu gibidir. Dolayısıyla, block1 ve block2 için farklı kuyruklar kullanırsanız veya aynı anda aynı kuyrukta zamanlarsanız, eşzamanlı olarak çalışabilirler; bunları aynı seri kuyruğunda zamanlarsanız, seri olarak çalışırlar. Grupları olmayan blokları zamanlamaktan farklı değildir.
Jörn Eyrich

1
Bu, web hizmeti yayınını yürütmek için de geçerli mi?
SleepNot

Zamanın bloğunuzda ayarlanan uyku zamanına eşit olmadığını fark ettiniz mi? neden böyle olurdu?
Damon Yuan

2
ARC sadece dispatch_release (grup) kaldırmak;
loretoparisi

272

Jörn Eyrich cevabını genişletmek (eğer bu cevabı yükseltirseniz cevabını yükseltin), dispatch_asyncbloklarınız için çağrılar üzerinde kontrolünüz yoksa, asenkron tamamlama blokları için olduğu gibi, GCD gruplarını dispatch_group_entervedispatch_group_leave doğrudan .

Bu örnekte, computeInBackgrounddeğiştiremeyeceğimiz bir şey gibi davranıyoruz (bir temsilci geri çağrısı, NSURLConnection completionHandler veya başka bir şey olduğunu hayal edin) ve bu nedenle gönderim çağrılarına erişimimiz yok.

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

Bu örnekte, computeInBackground: completion: şu şekilde uygulanır:

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

Çıktı (bir çalışmadan zaman damgaları ile):

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ɳ Yukarıdaki kod ana iş parçacığında bekliyor. Bunun ana iş parçacığını engelleyeceğine ve tüm grup tamamlanana kadar kullanıcı arayüzünün yanıt vermemesine neden olacağına inanıyorum. Beklemeyi bir arka plan iş parçacığına taşımanızı öneririz. Örneğin, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0)
cbartel

2
@cbartel, iyi yakala! Yorumunuzu yansıtacak şekilde örnek kodu güncelledim. Çoğu kez geri arama ana kuyrukta olması gerekir - bu durumda dispatch_queue_notifymuhtemelen daha iyi olsa da (engelleme süresi kısa olduğu garanti edilmezse).
Euroburɳ

Grubu nerede serbest bırakabilirim (yani dispatch_release (grup))? Dispatch_group_notify içinde yayınlamanın güvenli olup olmadığından emin değilim. Ancak grup tamamlandıktan sonra çalıştırılan kod olduğundan, nereye bırakacağımdan emin değilim.
GingerBreadMane

ARC kullanıyorsanız dispatch_release'i aramanız gerekmez: stackoverflow.com/questions/8618632/…
ɲeuroburɳ

3
Bunu daha iyi açıklayan güzel yazı: commandshift.co.uk/blog/2014/03/19/…
Rizon

98

Swift 5.1 ile Grand Central Dispatch , sorununuzu çözmek için birçok yol sunar. İhtiyaçlarınıza göre , aşağıdaki Oyun Alanı snippet'lerinde gösterilen yedi modelden birini seçebilirsiniz .


# 1. Kullanılması DispatchGroup, DispatchGroup'ler notify(qos:flags:queue:execute:)ve DispatchQueue' lerasync(group:qos:flags:execute:)

Apple Developer Eşzamanlılık Programlama Kılavuzu aşağıdakileri belirtirDispatchGroup :

Dağıtım grupları, bir veya daha fazla görev yürütmeyi bitirinceye kadar bir iş parçacığını engellemenin bir yoludur. Bu davranışı, belirtilen tüm görevler tamamlanana kadar ilerleme kaydedemeyeceğiniz yerlerde kullanabilirsiniz. Örneğin, bazı verileri hesaplamak için birkaç görev gönderdikten sonra, bu görevleri beklemek ve ardından tamamlandığında sonuçları işlemek için bir grup kullanabilirsiniz.

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. Kullanılması DispatchGroup, DispatchGroup's wait(), DispatchGroup' nin enter()ve DispatchGroup'lerleave()

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
 */

Ayrıca karışabileceğiyle Not DispatchGroup wait()ile DispatchQueue async(group:qos:flags:execute:)veya karıştırın DispatchGroup enter()ve DispatchGroup leave()birlikte DispatchGroup notify(qos:flags:queue:execute:).


3.. Ve 's kullanmaDispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Swift 4 için Grand Central Dispatch Tutorial: Raywenderlich.com'dan Bölüm 1/2 makalesi engelleri tanımlamaktadır :

Dağıtım engelleri, eşzamanlı kuyruklarla çalışırken seri tarzda bir darboğaz görevi gören bir grup işlevdir. Bir DispatchWorkItemgönderme kuyruğuna bir gönderirken, belirli bir süre için belirtilen kuyrukta yürütülen tek öğe olması gerektiğini belirtmek için bayraklar ayarlayabilirsiniz. Bu, gönderme engelinden önce kuyruğa gönderilen tüm öğelerin, DispatchWorkItemirade yürütülmeden önce tamamlanması gerektiği anlamına gelir .

Kullanımı:

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.. Kullanılması DispatchWorkItem, Dispatch​Work​Item​Flags'ler barrierve DispatchQueue' lerasync(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.. Kullanılıyor DispatchSemaphore, DispatchSemaphore'swait() ve DispatchSemaphore' lersignal()

Soroush Khanlou, GCD El Kitabı blog yazısına şu satırları yazdı :

Bir semafor kullanarak, bir iş parçacığını başka bir iş parçacığından gelen sinyal gönderilinceye kadar rastgele bir süre engelleyebiliriz. Semaforlar, GCD'nin geri kalanı gibi, iplik korumalıdır ve her yerden tetiklenebilirler. Semaforlar, senkronize etmeniz gereken asenkronize bir API olduğunda kullanılabilir, ancak değiştiremezsiniz.

Apple Developer API Reference ayrıca şu tartışmayı da sunar: DispatchSemaphore init(value:​) başlatıcı :

Değer için sıfır iletmek, iki iş parçacığının belirli bir olayın tamamlanmasını uzlaştırması gerektiğinde yararlıdır. Sıfırdan büyük bir değer iletmek, havuz boyutunun değere eşit olduğu sınırlı bir kaynak havuzunu yönetmek için kullanışlıdır.

Kullanımı:

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.. OperationQueueVe Operation's kullanmaaddDependency(_:)

Apple Developer API Referansı hakkında Operation​Queue :

İşlem kuyrukları libdispatch , işlemlerinin yürütülmesini başlatmak kütüphaneyi (Grand Central Dispatch olarak da bilinir) kullanır.

Kullanımı:

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.. OperationQueueVe OperationQueue's kullanma addBarrierBlock(_:)(iOS 13 gerektirir)

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
 */

Her biri için (ve semaforlar olmadan) group.enter () ve group.leave () kullanmadan zaman uyumsuz çağrılar için bir çözüm var mı? Gibi Bir sunucuya bir zaman uyumsuz istek beklemek gerekirse, o zaman sonra ikinci bir zaman uyumsuz istek ve benzeri için bekleyin. Bu makaleyi okudum avanderlee.com/swift/asynchronous-operations ama bunun BlockOperation
Woof

58

Başka bir GCD alternatifi bir engeldir:

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

Eşzamanlı bir kuyruk oluşturun, iki bloğunuzu gönderin ve son bloğu bariyere gönderin, bu da diğer ikisinin bitmesini bekler.


Uyku kullanmadıysam herhangi bir sorun var mı (4);
Himanth

Hayır, elbette, bununla ilgili bir sorun yok. Aslında, neredeyse hiç istemezsiniz sleep()! Ben sadece sleep()pedagojik nedenlerle bu çağrıları ekledim , blokların aynı anda çalıştığını görebilmeniz için yeterince uzun çalışmasını sağladım. Bu önemsiz örnekte, yokluğunda sleep(), bu iki blok o kadar hızlı çalışabilir ki, eşzamanlı yürütmeyi ampirik olarak gözlemleme şansınız olmadan gönderilen blok başlayabilir ve bitebilir. Ama sleep()kendi kodunuzda yok.
Soymak

39

GCD'yi sorduğunuzu biliyorum, ancak isterseniz, NSOperationQueuebu tür şeyleri gerçekten zarif bir şekilde ele alıyor, örneğin:

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
NSBlockOperation'ınızın içindeki kod senkron olduğunda bu sorun olmaz. Ama ya değilse ve zaman uyumsuz işleminiz tamamlandığında tamamlamayı tetiklemek istiyorsanız?
Greg Maletic

3
@GregMaletic Bu durumda, eşzamansız olan ve eşzamansız işlem tamamlandığında NSOperationayarlanan bir alt sınıf oluştururum isFinished. Sonra bağımlılıklar iyi çalışır.
Rob


1
@GregMaletic Evet, bunu da kullanabilirsiniz ( dispatch_semaphore_waitana kuyrukta yer almadığı ve sinyalleriniz ve beklemeleriniz dengeli olduğu sürece). Ana kuyruğu engellemediğiniz sürece, işlemlerin esnekliğine ihtiyacınız yoksa bir semafor yaklaşımı iyidir (örneğin, bunları iptal edebilme, eşzamanlılık derecesini kontrol etme yeteneği vb.).
Rob

1
@ Reza.Ab - İkinci görev başlamadan önce bir görev tamamlamanız gerekiyorsa, bu görevler arasında bir bağımlılık ekleyin. Sıranın her zaman sadece bir defada bir iş yapıyorsa Veya ayarlayarak bir seri kuyruk yapmak maxConcurrentOperationCountiçin 1. Hem qualityOfServiceve hem de işlemlerin önceliğini ayarlayabilirsiniz queuePriority, ancak bunların görev önceliği üzerinde bağımlılıklar ve / veya sıra eşzamanlılığının derecesinden çok daha ince bir etkisi vardır.
Rob

4

Yukarıdaki cevapların hepsi harika, ama hepsi bir şeyi kaçırdı. kullandığınızda girilen dizisindeki grup yürütür görevleri (blok) 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();
      }
    }

bu, oluşturulan eşzamanlı kuyrukta çalışır demoQueue. Herhangi bir kuyruk oluşturmazsanız, ana iş parçacığında çalışır .

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

ve görevleri başka bir iş parçacığında gerçekleştirmenin üçüncü bir yolu var:

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

Tabii ki, belirtildiği gibi dispatch_group_asyncistediğinizi elde etmek için kullanabilirsiniz .


3

İlk cevap esas olarak doğrudur, ancak istenen sonucu elde etmenin en basit yolunu istiyorsanız, burada bir semaforla nasıl yapılacağını gösteren bağımsız bir kod örneği (ayrıca, dağıtım gruplarının perde arkasında nasıl çalıştığı, 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
İki gözlem: 1. Eksik olan a dispatch_semaphore_wait. İki sinyaliniz var, bu yüzden iki beklemeye ihtiyacınız var. Olduğu gibi, "tamamlama" bloğunuz ilk blok semaforu işaret eder etmez, ancak diğer blok bitmeden başlayacaktır; 2. Bunun bir iOS sorusu olduğu düşünüldüğünde, kullanımını engellemem dispatch_main.
Rob

1
Rob'a katılıyorum. Bu geçerli bir çözüm değil. Bu yöntemlerden dispatch_semaphore_waitherhangi biri dispatch_semaphore_signalçağrıldığında engeli kaldırır . Bunun işe yaramasının nedeni, printf'one' ve 'two' blokları için hemen gerçekleşmesi ve printf'nihayet' için bir beklemeden sonra gerçekleşmesidir - böylece bloktan sonra 2 saniye uyudu. Printf'i sleeparamalardan sonra koyarsanız, çıkışı 'bir', sonra 2 saniye sonra 'nihayet' için, 2 saniye sonra 'iki' için alırsınız.
Euroburɳ

1

Hızlı bir şekilde kabul edilen cevap:

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 örneği:

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()kazaya neden oldu
Ben

-3

Diğer yanıtların belirli durumlar için harika olmadığını söylemek değil, ancak bu her zaman Google'dan kullanıcı olduğum bir snippet:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


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

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.