Menunggu hingga dua blok async dijalankan sebelum memulai blok lain


192

Saat menggunakan GCD, kami ingin menunggu sampai dua blok async dieksekusi dan dilakukan sebelum beralih ke langkah eksekusi selanjutnya. Apa cara terbaik untuk melakukannya?

Kami mencoba yang berikut, tetapi sepertinya tidak berhasil:

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

Lihat jawaban saya untuk Swift 5 yang menawarkan hingga enam cara berbeda untuk menyelesaikan masalah Anda.
Imanou Petit

Jawaban:


301

Gunakan grup pengiriman: lihat di sini untuk contoh, "Menunggu di Grup dari Tugas yang Di-antri" di bab "Pengiriman Antrian" di Panduan Pemrograman Concurrency Perpustakaan Pengembang Apple Apple.

Contoh Anda dapat terlihat seperti ini:

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

dan dapat menghasilkan output seperti ini:

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
Keren. akankah tugas / blok async, setelah dikaitkan dengan grup, dieksekusi secara berurutan atau bersamaan? Maksud saya, asumsikan bahwa block1 dan block2 dikaitkan dengan grup sekarang, akankah block2 menunggu sampai block1 selesai sebelum dapat mulai dieksekusi?
tom

9
Terserah kamu. dispatch_group_asyncsama seperti dispatch_asyncdengan parameter grup ditambahkan. Jadi jika Anda menggunakan antrian yang berbeda untuk block1 dan block2 atau menjadwalkannya pada antrian bersamaan yang sama, mereka dapat berjalan secara bersamaan; jika Anda menjadwalkannya pada antrian seri yang sama, mereka akan berjalan secara seri. Tidak ada bedanya dengan menjadwalkan blok tanpa grup.
Jörn Eyrich

1
Apakah ini juga berlaku untuk melaksanakan posting layanan web?
SleepNot

Apakah Anda memperhatikan bahwa waktunya tidak sama dengan waktu tidur yang ditetapkan di blok Anda? kenapa bisa seperti ini?
Damon Yuan

2
Di ARC hapus saja dispatch_release (group);
loretoparisi

272

Memperluas jawaban Jörn Eyrich (membatalkan jawaban jika Anda membatalkan jawaban ini), jika Anda tidak memiliki kendali atas dispatch_asyncpanggilan untuk blok Anda, seperti halnya untuk blok penyelesaian async, Anda dapat menggunakan grup GCD menggunakan dispatch_group_enterdandispatch_group_leave secara langsung.

Dalam contoh ini, kita berpura-pura computeInBackgroundadalah sesuatu yang tidak dapat kita ubah (bayangkan itu adalah panggilan balik delegasi, Penanganan sambungan NSURLConnection, atau apa pun), dan dengan demikian kita tidak memiliki akses ke panggilan pengiriman.

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

Dalam contoh ini, computeInBackground: completion: diimplementasikan sebagai:

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

Output (dengan cap waktu dari lari):

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ɳ Kode di atas menunggu di utas utama. Saya percaya ini akan memblokir utas utama dan menyebabkan UI menjadi tidak responsif sampai seluruh kelompok selesai. Saya merekomendasikan untuk memindahkan penantian ke latar belakang. Misalnya, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0)
cbartel

2
@ Bartel, tangkapan bagus! Saya telah memperbarui kode contoh untuk mencerminkan komentar Anda. Sering kali Anda membutuhkan panggilan balik untuk berada di antrian utama - dalam hal ini meskipun dispatch_queue_notifykemungkinan lebih baik (kecuali waktu pemblokiran dijamin pendek).
ɲeuroburɳ

Di mana saya dapat melepaskan grup (mis. Dispatch_release (grup))? Saya tidak yakin apakah aman untuk dirilis di dispatch_group_notify. Tapi karena itu kode yang dijalankan setelah grup selesai, saya tidak yakin ke mana harus merilis.
GingerBreadMane

Jika Anda menggunakan ARC maka Anda tidak perlu menelepon dispatch_release: stackoverflow.com/questions/8618632/…
ɲeuroburɳ

3
Posting bagus yang selanjutnya menjelaskan bahwa: commandshift.co.uk/blog/2014/03/19/…
Rizon

96

Dengan Swift 5.1, Grand Central Dispatch menawarkan banyak cara untuk menyelesaikan masalah Anda. Sesuai dengan kebutuhan Anda, Anda dapat memilih salah satu dari tujuh pola yang ditunjukkan dalam cuplikan Playground berikut.


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

Panduan Pemrograman Concurrency Pengembang Apple menyatakan tentangDispatchGroup :

Grup pengiriman adalah cara untuk memblokir utas sampai satu atau lebih tugas selesai dieksekusi. Anda dapat menggunakan perilaku ini di tempat-tempat di mana Anda tidak dapat membuat kemajuan sampai semua tugas yang ditentukan selesai. Misalnya, setelah mengirim beberapa tugas untuk menghitung beberapa data, Anda mungkin menggunakan grup untuk menunggu tugas-tugas itu dan kemudian memproses hasilnya ketika selesai.

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. Menggunakan DispatchGroup, DispatchGroup's wait(), DispatchGroup' enter()dan DispatchGroup'leave()

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

Perhatikan bahwa Anda juga dapat bergaul DispatchGroup wait()dengan DispatchQueue async(group:qos:flags:execute:)atau menggabungkan DispatchGroup enter()dan DispatchGroup leave()dengan DispatchGroup notify(qos:flags:queue:execute:).


# 3. Menggunakan dan 'sDispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Tutorial Grand Central Dispatch untuk Swift 4: Bagian 1/2 artikel dari Raywenderlich.com memberikan definisi untuk hambatan :

Hambatan pengiriman adalah sekelompok fungsi yang bertindak sebagai hambatan serial-style ketika bekerja dengan antrian bersamaan. Ketika Anda mengirimkan DispatchWorkItemke antrian pengiriman, Anda dapat mengatur bendera untuk menunjukkan bahwa itu harus menjadi satu-satunya item yang dieksekusi pada antrian yang ditentukan untuk waktu tertentu. Ini berarti bahwa semua item yang dikirim ke antrian sebelum penghalang pengiriman harus diselesaikan sebelum DispatchWorkItemakan dieksekusi.

Pemakaian:

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 Menggunakan DispatchWorkItem, Dispatch​Work​Item​Flags's barrierdan 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 Menggunakan DispatchSemaphore, DispatchSemaphore's wait()dan DispatchSemaphore' ssignal()

Soroush Khanlou menulis baris berikut dalam posting blog The GCD Handbook :

Dengan menggunakan semaphore, kita dapat memblokir utas untuk jumlah waktu yang sewenang-wenang, sampai sinyal dari utas lain dikirim. Semaphores, seperti GCD lainnya, adalah thread-safe, dan mereka dapat dipicu dari mana saja. Semaphores dapat digunakan ketika ada API asinkron yang perlu Anda sinkronkan, tetapi Anda tidak dapat memodifikasinya.

Referensi API Pengembang Apple juga memberikan diskusi berikut untuk DispatchSemaphore init(value:​)penginisialisasi:

Melewati nol untuk nilai berguna saat dua utas perlu merekonsiliasi penyelesaian acara tertentu. Melewati nilai yang lebih besar dari nol berguna untuk mengelola kumpulan sumber daya yang terbatas, di mana ukuran kumpulan sama dengan nilai.

Pemakaian:

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. Menggunakan OperationQueuedan Operation'saddDependency(_:)

Referensi API Pengembang Apple menyatakan tentang Operation​Queue:

Antrian operasi menggunakan libdispatchperpustakaan (juga dikenal sebagai Grand Central Dispatch) untuk memulai pelaksanaan operasi mereka.

Pemakaian:

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. Menggunakan OperationQueuedan OperationQueue's addBarrierBlock(_:)(membutuhkan 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
 */

Apakah ada solusi untuk panggilan async tanpa menggunakan group.enter () dan group.leave () untuk masing-masing (dan tanpa semaphores)? Seperti Jika saya perlu menunggu permintaan async ke server, maka setelah itu tunggu permintaan async kedua dan seterusnya. Saya telah membaca artikel ini avanderlee.com/swift/asynchronous-operations tapi saya tidak melihat penggunaannya yang sederhana dibandingkan dengan BlockOperation
Woof

58

Alternatif GCD lainnya adalah penghalang:

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

Cukup buat antrian bersamaan, kirim dua blok Anda, dan kirim blok terakhir dengan penghalang, yang akan membuatnya menunggu dua lainnya selesai.


Apakah ada masalah jika saya tidak menggunakan tidur (4);
Himanth

Tidak, tentu saja, tidak ada masalah dengan itu. Bahkan, Anda praktis tidak pernah mau sleep()! Saya hanya menambahkan sleep()panggilan itu untuk alasan pedagogis, untuk membuat blok berjalan cukup lama sehingga Anda dapat melihat bahwa mereka berjalan secara bersamaan. Dalam contoh sepele ini, jika tidak ada sleep(), dua blok ini dapat berjalan sangat cepat sehingga blok yang dikirim mungkin mulai dan selesai sebelum Anda memiliki kesempatan untuk secara empiris mengamati eksekusi bersamaan. Tapi jangan sleep()di kode Anda sendiri.
Rob

39

Saya tahu Anda bertanya tentang GCD, tetapi jika Anda mau, NSOperationQueuejuga menangani hal-hal semacam ini dengan sangat anggun, misalnya:

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
Ini bagus ketika kode di dalam NSBlockOperation Anda sinkron. Tetapi bagaimana jika tidak, dan Anda ingin memicu penyelesaian ketika operasi async Anda selesai?
Greg Maletic

3
@GregMaletic Dalam hal itu, saya membuat NSOperationsubkelas yang bersamaan dan diatur isFinishedketika proses asinkron selesai. Kemudian dependensi berfungsi dengan baik.
Rob


1
@GregMaletic Ya, Anda bisa menggunakannya juga (selama dispatch_semaphore_waittidak terjadi pada antrian utama dan selama sinyal dan menunggu Anda seimbang). Selama Anda tidak memblokir antrian utama, pendekatan semaphore baik-baik saja, jika Anda tidak memerlukan fleksibilitas operasi (misalnya memiliki kemampuan untuk membatalkannya, kemampuan untuk mengontrol tingkat konkurensi, dll).
Rob

1
@ Reza.Ab - Jika Anda membutuhkan tugas satu untuk menyelesaikan sebelum tugas kedua dimulai, tambahkan ketergantungan di antara tugas-tugas itu. Atau jika antrian selalu hanya melakukan satu tugas pada satu waktu, buatlah itu sebagai antrian serial dengan mengatur maxConcurrentOperationCountke 1. Anda dapat menetapkan prioritas operasi, juga, qualityOfServicedan queuePriority, tetapi ini memiliki dampak yang jauh lebih halus pada prioritas tugas daripada dependensi dan / atau tingkat konkurensi antrian.
Rob

4

Jawaban di atas semua keren, tetapi mereka semua melewatkan satu hal. grup menjalankan tugas (blok) di utas tempat masuknya ketika Anda menggunakan 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();
      }
    }

ini berjalan dalam antrian bersamaan yang dibuat demoQueue. Jika saya tidak membuat antrian, itu berjalan di utas utama .

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

dan ada cara ketiga untuk membuat tugas dijalankan di utas lain:

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

Tentu saja, seperti yang disebutkan dapat Anda gunakan dispatch_group_asyncuntuk mendapatkan apa yang Anda inginkan.


3

Jawaban pertama pada dasarnya benar, tetapi jika Anda ingin cara paling sederhana untuk mencapai hasil yang diinginkan, berikut ini adalah contoh kode yang berdiri sendiri yang menunjukkan cara melakukannya dengan semaphore (yang juga merupakan cara pengiriman grup bekerja di belakang layar, 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
Dua pengamatan: 1. Anda hilang a dispatch_semaphore_wait. Anda memiliki dua sinyal, jadi Anda perlu dua menunggu. Seperti, blok "selesai" Anda akan mulai segera setelah blok pertama memberi sinyal pada semaphore, tetapi sebelum blok lainnya selesai; 2. Mengingat ini adalah pertanyaan iOS, saya tidak akan menyarankan penggunaan dispatch_main.
Rob

1
Saya setuju dengan Rob. Ini bukan solusi yang valid. The dispatch_semaphore_waitakan membuka blokir sesegera salah satu dispatch_semaphore_signalmetode yang disebut. Alasan mengapa hal ini tampaknya berhasil adalah printfkarena blok 'satu' dan 'dua' terjadi segera, dan printfuntuk 'akhirnya' terjadi setelah menunggu - dengan demikian setelah blok satu telah tidur selama 2 detik. Jika Anda meletakkan printf setelah sleeppanggilan, Anda akan mendapatkan output untuk 'satu', lalu 2 detik kemudian untuk 'akhirnya', lalu 2 detik kemudian untuk 'dua'.
ɲeuroburɳ

1

Jawaban yang diterima dengan cepat:

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

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

group.leave()menyebabkan crash
Ben

-3

Bukan untuk mengatakan jawaban lain tidak bagus untuk keadaan tertentu, tetapi ini adalah satu cuplikan saya selalu pengguna dari Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


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

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.