Ожидание выполнения двух асинхронных блоков перед запуском другого блока
при использовании GCD мы хотим подождать, пока не будут выполнены два асинхронных блока, прежде чем переходить к следующим шагам выполнения. Каков наилучший способ сделать это?
мы пробовали следующее, Но это, кажется, не работает:
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
});
8 ответов:
используйте группы отправки: см. здесь например, "ожидание групп поставленных в очередь задач" в главе "очереди отправки" руководства по программированию параллелизма библиотеки разработчиков iOS от Apple
ваш пример может выглядеть так:
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);и может производить вывод такой:
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 ответ (upvote его ответ, если вы upvote этот), если у вас нет контроля над
dispatch_asyncвызовы для ваших блоков, как и в случае асинхронных блоков завершения, вы можете использовать группы GCD с помощьюdispatch_group_enterиdispatch_group_leaveнапрямую.в этом примере мы притворяемся
computeInBackgroundэто то, что мы не можем изменить (представьте, что это обратный вызов делегата, NSURLConnection completionHandler или что-то еще), и поэтому у нас нет доступа к отправке звонки.// 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!");в этом примере computeInBackground: completion: реализуется как:
- (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(); }); }вывод (с отметками времени от запуска):
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!
Другой альтернативой GCD является барьер:
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"); });просто создайте параллельную очередь, отправьте два блока, а затем отправьте последний блок с барьером, который заставит его ждать завершения двух других.
С Swift 3, Grand Central Dispatch предлагает много способов решить вашу проблему. В соответствии с вашими потребностями, вы можете выбрать один из шесть моделей показано в следующих фрагментах площадка.
#1. Используя
DispatchGroup,DispatchGroupnotify(qos:flags:queue:execute:)иDispatchQueueasync(group:qos:flags:execute:)методыРуководство По Программированию Параллелизма Разработчика Apple государств о
DispatchGroup:группы диспетчеризации-это способ блокировать поток до завершения выполнения одной или нескольких задач. Вы можете использовать такое поведение в местах, где вы не можете добиться прогресса, пока все указанные задачи. Например, после отправки нескольких задач для вычисления некоторых данных вы можете использовать группу для ожидания этих задач, а затем обработать результаты, когда они будут выполнены.
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. Используя
DispatchGroup,DispatchGroupwait(),DispatchGroupenter()и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 */обратите внимание, что вы также можете смешивать
DispatchGroupwait()СDispatchQueueasync(group:qos:flags:execute:)или mixDispatchGroupenter()иDispatchGroupleave()СDispatchGroupnotify(qos:flags:queue:execute:).
#3. Используя
DispatchWorkItemFlagsbarrierсобственность иDispatchQueueDispatchQueueasync(group:qos:flags:execute:)методGrand Central Dispatch учебник для Swift 3: Часть 1/2 статья из Raywenderlich.com дает определение для препятствия:
барьеры отправки-это группа функций, действующих как узкое место последовательного типа при работе с параллельными очередями. [...] Когда вы отправляете
DispatchWorkItemдля очереди отправки вы можете установить флаги, чтобы указать, что это должен быть единственный элемент, выполняемый в указанной очереди для этого конкретного время. Это означает, что все элементы, отправленные в очередь до барьера отправки, должны быть завершены доDispatchWorkItemбудет выполнять.использование:
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. Используя
DispatchWorkItem,DispatchWorkItemFlagsbarrierсобственность иDispatchQueueasync(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. Используя
DispatchSemaphore,DispatchSemaphorewait()иDispatchSemaphoresignal()методыСоруш Khanlou написал следующие строки руководство GCD блог:
используя семафор, мы можем блокировать поток на произвольное время, пока не будет отправлен сигнал из другого потока. Семафоры, как и остальные GCD, являются потокобезопасными, и они могут быть вызваны из любого места. Семафоры могут использоваться, когда есть асинхронный API, который вам нужно сделать синхронным, но вы не можете изменить его.
Apple Developer API Reference также дает следующее обсуждение для
DispatchSemaphoreinit(value:)инициализатор:передача нуля для значения полезна, когда два потока должны согласовать завершение определенного события. Передача значения больше нуля полезна для управления конечным пулом ресурсов, где размер пула равен значение.
использование:
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. Используя
OperationQueueиBlockOperationв справочнике Apple Developer API говорится о
OperationQueue:очереди операций используют
libdispatchбиблиотека (также известная как Grand Central Dispatch) для инициирования выполнения своих операций.использование:
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 */
Я знаю, что вы спрашивали о GCD, но если вы хотите,
NSOperationQueueтакже обрабатывает такого рода вещи очень изящно, например: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];
первый ответ по существу правильный, но если вы хотите самый простой способ достичь желаемого результата, вот автономный пример кода, демонстрирующий, как это сделать с семафором (который также является тем, как группы отправки работают за кулисами, 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(); }
ответы выше все круто, но все они пропустили одну вещь. группа выполняет задачи (блоки) в потоке, где она введена при использовании
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(); } }это работает в созданной параллельной очереди
demoQueue. Если я не создаю очередь, она работает в основной поток.- (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(); } }и есть третий способ сделать задачи, выполняемые в другом потоке:
- (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!"); }); // }); }конечно, как уже упоминалось, вы можете использовать
dispatch_group_asyncчтобы получить то, что вы хотеть.
Не сказать, что другие ответы не очень хороши для определенных обстоятельств, но это один фрагмент я всегда пользователь из Google:
- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel { if (signInDoneSel) { [self performSelector:signInDoneSel]; } }
Comments