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


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, DispatchGroupnotify(qos:flags:queue:execute:)DispatchQueueasync(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, DispatchGroupwait(), DispatchGroupenter()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​FlagsbarrierDispatchQueueasync(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()

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. 사용 OperationQueueOperationaddDependency(_:)

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. 사용 OperationQueueOperationQueueaddBarrierBlock(_:)(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.