다른 블록을 시작하기 전에 두 개의 비동기 블록이 실행될 때까지 대기


192

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

문제를 해결하는 최대 6 가지 방법을 제공하는 Swift 5에 대한 내 답변 을 참조하십시오 .
— Imanou Petit

답변:


301

디스패치 그룹 사용 : Apple iOS 개발자 라이브러리 동시성 프로그래밍 안내서의 "큐 대기"장의 "대기중인 작업 그룹 대기"에 대한 예제는 여기 를 참조 하십시오 .

귀하의 예는 다음과 같습니다.

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

3
멋있는. 그룹과 연결된 비동기 작업 / 블록이 순차적으로 또는 동시에 실행됩니까? 내 말은, block1과 block2가 이제 그룹과 연결되어 있다고 가정하면 block2가 block1이 완료 될 때까지 기다렸다가 실행을 시작할 수 있습니까?
— tom

9
그것은 당신에게 달려 있습니다. 그룹 매개 변수가 추가 된 dispatch_group_async것과 같습니다 dispatch_async. 따라서 block1과 block2에 서로 다른 대기열을 사용하거나 동일한 동시 대기열에서 예약하면 동시에 실행될 수 있습니다. 동일한 직렬 대기열에서 예약하면 직렬로 실행됩니다. 그룹없이 블록을 예약하는 것과 다르지 않습니다.
— Jörn Eyrich

1
웹 서비스 게시 실행에도 적용됩니까?
— SleepNot

시간이 블록에 설정된 절전 시간과 같지 않습니까? 왜 이렇게 될까요?
— Damon Yuan

2
ARC에서 dispatch_release (group)을 제거하십시오.
— loretoparisi

272

dispatch_async비동기 완료 블록의 경우와 같이 블록 호출을 제어 할 수없는 경우, Jörn Eyrich 응답에서 확장 (자신의 응답을 찬성하면 그의 답을 찬성) 하십시오. 완료 완료 블록의 경우 dispatch_group_enter와 마찬가지로 GCD 그룹을 사용 하여 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!

1
@ ɲeuroburɳ 위 코드는 메인 스레드에서 대기합니다. 이것이 메인 스레드를 차단하고 전체 그룹이 완료 될 때까지 UI가 응답하지 않게 할 것이라고 믿습니다. 대기를 백그라운드 스레드로 이동하는 것이 좋습니다. 예를 들어, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0)
— cbartel

2
@ cbartel, 잘 잡아! 귀하의 의견을 반영하기 위해 예제 코드를 업데이트했습니다. 대부분의 경우 콜백이 메인 큐에 있어야합니다.이 경우 dispatch_queue_notify블로킹 시간이 단축되지 않는 한 더 좋습니다.
— ɲ euroburɳ

그룹을 어디에서 해제 할 수 있습니까 (예 : dispatch_release (group))? dispatch_group_notify에서 릴리스하는 것이 안전한지 확실하지 않습니다. 그러나 이것이 그룹이 완료 된 후에 실행되는 코드이므로 릴리스 할 위치가 확실하지 않습니다.
— GingerBreadMane

당신은 ARC를 사용하는 경우에 당신은 전화 dispatch_release 할 필요가 없습니다 : stackoverflow.com/questions/8618632/...을
— ɲeuroburɳ

3
더 자세한 설명이있는 멋진 게시물 : commandshift.co.uk/blog/2014/03/19/…
— Rizon

98

Swift 5.1에서 Grand Central Dispatch 는 문제를 해결하는 여러 가지 방법을 제공합니다. 필요 에 따라 다음 놀이터 스 니펫에 표시된 7 가지 패턴 중 하나를 선택할 수 있습니다 .


#1. 사용 DispatchGroup, DispatchGroup의 notify(qos:flags:queue:execute:)와 DispatchQueue의async(group:qos:flags:execute:)

Apple Developer Concurrency Programming Guide는 다음에 대해 설명합니다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, DispatchGroup의 wait(), DispatchGroup의 enter()와 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
 */

당신은 또한 혼합 할 수 있습니다 DispatchGroup wait()으로 DispatchQueue async(group:qos:flags:execute:)또는 혼합 DispatchGroup enter()하고 DispatchGroup leave()와 DispatchGroup notify(qos:flags:queue:execute:).


#삼. 사용 및 의Dispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Swift 4 : Grand Central Dispatch Tutorial : Raywenderlich.com의 1/2 부 기사는 장벽에 대한 정의를 제공합니다 .

디스패치 배리어는 동시 큐 작업시 직렬 스타일 병목 현상으로 작동하는 기능 그룹입니다. 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, Dispatch​Work​Item​Flags의 barrier와 DispatchQueue의async(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, DispatchSemaphore의 wait()와 DispatchSemaphore의signal()

Soroush Khanlou는 GCD 핸드북 블로그 게시물 에 다음과 같은 내용을 썼습니다 .

세마포를 사용하면 다른 스레드의 신호가 전송 될 때까지 임의의 시간 동안 스레드를 차단할 수 있습니다. 나머지 GCD와 마찬가지로 세마포어는 스레드로부터 안전하며 어디에서나 트리거 할 수 있습니다. 세마포어는 동기식으로 만들어야하는 비동기식 API가있는 경우 사용할 수 있지만 수정할 수는 없습니다.

Apple Developer API Reference는 DispatchSemaphore init(value:​)이니셜 라이저에 대한 다음 설명도 제공합니다 .

값에 0을 전달하면 두 스레드가 특정 이벤트의 완료를 조정해야 할 때 유용합니다. 0보다 큰 값을 전달하면 한정된 리소스 풀을 관리하는 데 유용합니다. 여기서 풀 크기는 값과 같습니다.

용법:

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및 Operation의addDependency(_:)

Apple Developer API Reference는 다음에 대해 설명합니다 Operation​Queue.

작업 대기열은 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
 */

# 7. 사용 OperationQueue및 OperationQueue의 addBarrierBlock(_:)(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
 */

각각에 대해 (세마포가없는) group.enter () 및 group.leave ()를 사용하지 않고 비동기 호출에 대한 솔루션이 있습니까? 서버에 대한 비동기 요청을 기다려야하는 경우와 같이 두 번째 비동기 요청을 기다립니다. 이 기사를 읽었습니다 avanderlee.com/swift/asynchronous-operations 그러나 BlockOperation
— Woof

58

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

동시 대기열을 생성하고 두 블록을 디스패치 한 다음 장벽과 함께 마지막 블록을 디스패치하면 다른 두 블록이 끝날 때까지 기다릴 수 있습니다.


sleep (4)를 사용하지 않으면 문제가 있습니까?
— Himanth

물론 문제는 없습니다. 실제로, 당신은 실제로 절대 원하지 않습니다 sleep()! 나는 sleep()교육적인 이유에 대한 호출을 추가 하여 블록이 충분히 오래 실행되어 동시에 실행되는 것을 볼 수 있도록했습니다. 이 간단한 예에서는이 sleep()두 블록이 너무 빨리 실행되어 동시 실행을 경험적으로 관찰하기 전에 디스패치 된 블록이 시작 및 종료 될 수 있습니다. 그러나 sleep()자신의 코드로 작성 하지 마십시오 .
— Rob

39

나는 당신이 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];

3
NSBlockOperation 내부의 코드가 동기적일 때 좋습니다. 그러나 그렇지 않은 경우 비동기 작업이 완료 될 때 완료를 트리거하려면 어떻게해야합니까?
— Greg Maletic

3
@GregMaletic이 경우 비동기 프로세스가 완료되면 NSOperation동시 서브 클래스를 만들고 설정 isFinished합니다. 그런 다음 종속성이 제대로 작동합니다.
— Rob

@GregMaletic 예제는 stackoverflow.com/questions/18429011/… 및 stackoverflow.com/questions/17426855/… 를 참조하십시오 .
— Rob

1
@GregMaletic 그래, dispatch_semaphore_wait메인 큐에서 발생하지 않는 한, 신호와 대기의 균형이 유지되는 한도 사용할 수 있습니다 . 주 대기열을 차단하지 않는 한 작업의 유연성 (예 : 취소 기능, 동시성 제어 기능 등)이 필요하지 않은 경우 세마포어 접근 방식이 좋습니다.
— Rob

1
@ Reza.Ab-작업 2가 시작되기 전에 작업 1을 완료해야하는 경우 해당 작업 사이에 종속성을 추가하십시오. 또는 대기열이 항상 한 번에 하나의 작업 만 수행하는 경우로 설정 maxConcurrentOperationCount하여 직렬 대기열로 만드십시오 1. qualityOfService및 의 작업 우선 순위를 설정할 수도 queuePriority있지만, 종속성 및 / 또는 대기열 동시성 정도보다 작업 우선 순위에 훨씬 미묘한 영향을 미칩니다.
— Rob

4

위의 답변은 모두 멋지지만 모두 한 가지를 놓쳤습니다. 그룹은 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원하는 것을 얻을 수 있습니다 .


3

첫 번째 대답은 본질적으로 정확하지만 원하는 결과를 얻는 가장 간단한 방법을 원한다면 세마포어로 작업을 수행하는 방법을 보여주는 독립 실행 형 코드 예제가 있습니다 (이것은 디스패치 그룹이 장면 뒤에서 작동하는 방식 인 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
두 가지 관찰 : 1.을 놓쳤습니다 dispatch_semaphore_wait. 두 개의 신호가 있으므로 두 개의 대기가 필요합니다. "완료"블록은 첫 번째 블록이 세마포에 신호를 보내 자마자 다른 블록이 끝나기 전에 시작됩니다. 2. 이것이 iOS 질문이므로을 사용하지 않는 것이 좋습니다 dispatch_main.
— Rob

1
Rob에 동의합니다. 이것은 유효한 솔루션이 아닙니다. 는 dispatch_semaphore_wait즉시 중 하나로서 차단을 해제 할 dispatch_semaphore_signal방법이라고합니다. 이것이 작동하는 것처럼 보일 수있는 이유는 printffor 블록 '1'과 '2'가 즉시 발생 printf하고 '최종'에 대한 대기 후-블록 1이 2 초 동안 잠든 후에 발생하기 때문입니다. sleep호출 후에 printf를 넣으면 'one'에 대한 출력을 얻은 다음 2 초 후에 'finally'에 대해 2 초 후에 'two'에 대한 출력을 얻게됩니다.
— ɲ euroburɳ

1

신속한 답변 허용 :

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

스위프트 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()충돌 발생
— Ben

-3

특정 상황에서는 다른 답변이 좋지 않다고 말할 수는 없지만 이것은 항상 Google 사용자입니다.

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


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

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.