Chờ cho đến khi hai khối async được thực thi trước khi bắt đầu một khối khác


192

Khi sử dụng GCD, chúng tôi muốn đợi cho đến khi hai khối async được thực thi và thực hiện trước khi chuyển sang các bước thực hiện tiếp theo. Cách tốt nhất để làm điều đó là gì?

Chúng tôi đã thử cách sau, nhưng có vẻ như không hoạt động:

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

Xem câu trả lời của tôi cho Swift 5 cung cấp tối đa sáu cách khác nhau để giải quyết vấn đề của bạn.
Imanou Petit

Câu trả lời:


301

Sử dụng các nhóm công văn: xem tại đây để biết ví dụ: "Chờ đợi các nhóm nhiệm vụ được xếp hàng" trong chương "Hàng đợi điều chỉnh" trong Hướng dẫn lập trình đồng thời của Thư viện nhà phát triển iOS của Apple

Ví dụ của bạn có thể trông giống như thế này:

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

và có thể tạo ra sản lượng như thế này:

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
Mát mẻ. các nhiệm vụ / khối không đồng bộ, một khi được liên kết với nhóm, sẽ được thực hiện tuần tự hay đồng thời? Ý tôi là, giả sử rằng block1 và block2 được liên kết với một nhóm bây giờ, block2 sẽ đợi cho đến khi block1 được thực hiện trước khi nó có thể bắt đầu thực thi?
tom

9
Tùy bạn. dispatch_group_asyncgiống như dispatch_asyncvới một tham số nhóm được thêm vào. Vì vậy, nếu bạn sử dụng các hàng đợi khác nhau cho block1 và block2 hoặc lên lịch cho chúng trên cùng một hàng đợi đồng thời, chúng có thể chạy đồng thời; nếu bạn lên lịch cho chúng trên cùng một hàng đợi nối tiếp, chúng sẽ chạy một cách an toàn. Không khác gì lập lịch cho các khối mà không có nhóm.
Jorn Eyrich

1
Điều này cũng áp dụng để thực hiện bài dịch vụ web?
Ngủ không

Bạn có nhận thấy rằng thời gian không bằng thời gian ngủ được đặt trong khối của bạn không? Tại sao nó lại như thế này?
Damon Yuan

2
Trong ARC chỉ cần loại bỏ Clark_release (nhóm);
loretoparisi

272

Mở rộng câu trả lời của Jorn Eyrich (nâng cao câu trả lời của anh ấy nếu bạn nâng cấp câu hỏi này), nếu bạn không kiểm soát các dispatch_asynccuộc gọi cho các khối của mình, như trường hợp của các khối hoàn thành không đồng bộ, bạn có thể sử dụng dispatch_group_entervà sử dụng dispatch_group_leavetrực tiếp các nhóm GCD .

Trong ví dụ này, chúng tôi giả vờ computeInBackgroundlà thứ chúng tôi không thể thay đổi (hãy tưởng tượng đó là cuộc gọi lại của đại biểu, NSURLConnection hoàn thành Trình xử lý hoặc bất cứ điều gì), và do đó chúng tôi không có quyền truy cập vào các cuộc gọi công văn.

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

Trong ví dụ này, computeInBackground: xong: được triển khai như sau:

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

Đầu ra (với dấu thời gian từ một lần chạy):

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ɳ Đoạn mã trên chờ trên luồng chính. Tôi tin rằng điều này sẽ chặn luồng chính và khiến UI không phản hồi cho đến khi toàn bộ nhóm hoàn thành. Tôi khuyên bạn nên di chuyển chờ đợi đến một chủ đề nền. Ví dụ:
Clark_get_global_queue

2
@cbartel, bắt tốt! Tôi đã cập nhật mã ví dụ để phản ánh nhận xét của bạn. Nhiều lần bạn cần gọi lại để được xếp hàng chính - trong trường hợp đó mặc dù dispatch_queue_notifycó khả năng tốt hơn (trừ khi thời gian chặn được đảm bảo là ngắn).
ɲeuroburɳ

Tôi có thể phát hành nhóm ở đâu (tức là Clark_release (nhóm))? Tôi không chắc chắn liệu nó có an toàn để phát hành trong Clark_group_notify không. Nhưng vì đó là mã được chạy sau khi nhóm hoàn thành, tôi không chắc sẽ phát hành ở đâu.
GingerBreadMane

Nếu bạn đang sử dụng ARC thì bạn không cần phải gọi
Clark_release

3
Đẹp bài mà còn giải thích thêm rằng: commandshift.co.uk/blog/2014/03/19/...
Rizon

96

Với Swift 5.1, Grand Central Dispatch cung cấp nhiều cách để giải quyết vấn đề của bạn. Theo nhu cầu của bạn, bạn có thể chọn một trong bảy mẫu được hiển thị trong các đoạn Playground sau.


# 1. Sử dụng DispatchGroup, DispatchGroup's notify(qos:flags:queue:execute:)DispatchQueue' sasync(group:qos:flags:execute:)

Hướng dẫn lập trình đồng thời dành cho nhà phát triển của Apple nêu vềDispatchGroup :

Các nhóm điều phối là một cách để chặn một luồng cho đến khi một hoặc nhiều tác vụ hoàn thành thực thi. Bạn có thể sử dụng hành vi này ở những nơi bạn không thể thực hiện tiến trình cho đến khi tất cả các nhiệm vụ được chỉ định hoàn tất. Ví dụ: sau khi gửi một số tác vụ để tính toán một số dữ liệu, bạn có thể sử dụng một nhóm để chờ các tác vụ đó và sau đó xử lý kết quả khi chúng được thực hiện.

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. Sử dụng DispatchGroup, DispatchGroup's wait(), DispatchGroup' enter()DispatchGroup'sleave()

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

Lưu ý rằng bạn cũng có thể trộn DispatchGroup wait()với DispatchQueue async(group:qos:flags:execute:)hoặc trộn DispatchGroup enter()DispatchGroup leave()với DispatchGroup notify(qos:flags:queue:execute:).


# 3. Sử dụng và 'sDispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Hướng dẫn về công văn Grand Central cho Swift 4: Phần 1/2 bài viết từ Raywenderlich.com đưa ra định nghĩa cho các rào cản :

Rào cản công văn là một nhóm các chức năng hoạt động như một nút cổ chai kiểu nối tiếp khi làm việc với các hàng đợi đồng thời. Khi bạn gửi một DispatchWorkItemhàng đợi công văn, bạn có thể đặt cờ để chỉ ra rằng đó phải là mục duy nhất được thực thi trên hàng đợi đã chỉ định cho thời gian cụ thể đó. Điều này có nghĩa là tất cả các mục được gửi đến hàng đợi trước hàng rào công văn phải hoàn thành trước khi DispatchWorkItemthực hiện.

Sử dụng:

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. Sử dụng DispatchWorkItem, Dispatch​Work​Item​Flags's barrierDispatchQueue' 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. Sử dụng DispatchSemaphore, DispatchSemaphore's wait()DispatchSemaphore' ssignal()

Soroush Khanlou đã viết những dòng sau trong bài viết trên blog của Sổ tay GCD :

Sử dụng một semaphore, chúng ta có thể chặn một luồng trong một khoảng thời gian tùy ý, cho đến khi tín hiệu từ một luồng khác được gửi. Semaphores, giống như phần còn lại của GCD, an toàn cho chuỗi và chúng có thể được kích hoạt từ bất cứ đâu. Semaphores có thể được sử dụng khi có API không đồng bộ mà bạn cần tạo đồng bộ, nhưng bạn không thể sửa đổi nó.

Tham khảo API dành cho nhà phát triển của Apple cũng đưa ra các cuộc thảo luận sau cho trình DispatchSemaphore init(value:​)khởi tạo:

Vượt qua 0 cho giá trị là hữu ích khi hai luồng cần điều hòa việc hoàn thành một sự kiện cụ thể. Truyền một giá trị lớn hơn 0 rất hữu ích để quản lý nhóm tài nguyên hữu hạn, trong đó kích thước nhóm bằng với giá trị.

Sử dụng:

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. Sử dụng OperationQueueOperation'saddDependency(_:)

Tham chiếu API dành cho nhà phát triển của Apple nêu về Operation​Queue:

Hàng đợi hoạt động sử dụng libdispatchthư viện (còn được gọi là Grand Central Dispatch) để bắt đầu thực hiện các hoạt động của họ.

Sử dụng:

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. Sử dụng OperationQueueOperationQueue's addBarrierBlock(_:)(yêu cầu 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
 */

Có giải pháp nào cho các cuộc gọi không đồng bộ mà không sử dụng group.enter () và group.leave () cho mỗi (và không có semaphores) không? Giống như Nếu tôi cần đợi một yêu cầu async đến máy chủ, thì sau đó chờ một yêu cầu async thứ hai, v.v. Tôi đã đọc bài viết này avanderlee.com/swift/asynchronous-operations nhưng tôi don xem một cách sử dụng đơn giản của nó so với BlockOperation
Woof

58

Một thay thế GCD khác là một rào cản:

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

Chỉ cần tạo một hàng đợi đồng thời, gửi hai khối của bạn, và sau đó gửi khối cuối cùng với rào chắn, điều này sẽ làm cho nó chờ hai khối kia kết thúc.


Có vấn đề gì không nếu tôi không sử dụng giấc ngủ (4);
Himanth

Không, tất nhiên, không có vấn đề với điều đó. Trong thực tế, bạn thực tế không bao giờ muốn sleep()! Tôi chỉ thêm những sleep()lời kêu gọi đó vì lý do sư phạm, để làm cho các khối chạy đủ lâu để bạn có thể thấy rằng chúng chạy đồng thời. Trong ví dụ tầm thường này, trong trường hợp không có sleep(), hai khối này có thể chạy nhanh đến mức khối được gửi có thể bắt đầu và kết thúc trước khi bạn có cơ hội quan sát thực nghiệm việc thực hiện đồng thời. Nhưng đừng sleep()trong mã của riêng bạn.
Rob

39

Tôi biết bạn đã hỏi về GCD, nhưng nếu bạn muốn, NSOperationQueuecũng xử lý loại công cụ này thực sự duyên dáng, ví dụ:

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
Điều này tốt khi mã bên trong NSBlockOperation của bạn đồng bộ. Nhưng nếu không, và bạn muốn kích hoạt hoàn thành khi hoạt động không đồng bộ của bạn được thực hiện thì sao?
Greg Maletic

3
@GregMaletic Trong trường hợp đó, tôi tạo một NSOperationlớp con đồng thời và được đặt isFinishedkhi quá trình không đồng bộ hoàn thành. Sau đó các phụ thuộc làm việc tốt.
Cướp


1
@GregMaletic Vâng, bạn cũng có thể sử dụng điều đó (miễn dispatch_semaphore_waitlà không diễn ra trên hàng đợi chính và miễn là tín hiệu và sự chờ đợi của bạn được cân bằng). Miễn là bạn không chặn hàng đợi chính, cách tiếp cận semaphore là tốt, nếu bạn không cần sự linh hoạt của các hoạt động (ví dụ: có khả năng hủy chúng, khả năng kiểm soát mức độ đồng thời, v.v.).
Cướp

1
@ Reza.Ab - Nếu bạn cần nhiệm vụ một để hoàn thành trước khi bắt đầu nhiệm vụ hai, hãy thêm một phụ thuộc giữa các nhiệm vụ đó. Hoặc nếu hàng đợi luôn chỉ thực hiện một nhiệm vụ tại một thời điểm, hãy đặt nó thành hàng đợi nối tiếp bằng cách đặt maxConcurrentOperationCountthành 1. Bạn cũng có thể đặt mức độ ưu tiên của các hoạt động, cả hai qualityOfServicequeuePriority, nhưng chúng có tác động tinh tế hơn nhiều đối với mức độ ưu tiên của nhiệm vụ so với các phụ thuộc và / hoặc mức độ đồng thời của hàng đợi.
Cướp

4

Câu trả lời ở trên đều tuyệt vời, nhưng tất cả đều bỏ lỡ một điều. nhóm thực thi các tác vụ (khối) trong luồng mà nó đã nhập khi bạn sử dụng 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();
      }
    }

cái này chạy trong hàng đợi đồng thời được tạo demoQueue. Nếu tôi không tạo ra bất kỳ hàng đợi nào, nó sẽ chạy trong luồng chính .

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

và có một cách thứ ba để thực hiện các tác vụ trong một luồng khác:

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

Tất nhiên, như đã đề cập, bạn có thể sử dụng dispatch_group_asyncđể có được những gì bạn muốn.


3

Câu trả lời đầu tiên về cơ bản là chính xác, nhưng nếu bạn muốn cách đơn giản nhất để đạt được kết quả mong muốn, thì đây là một ví dụ mã độc lập minh họa cách thực hiện với semaphore (cũng là cách các nhóm điều hành hoạt động đằng sau hậu trường, 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
Hai quan sát: 1. Bạn đang thiếu a dispatch_semaphore_wait. Bạn có hai tín hiệu, vì vậy bạn cần hai chờ đợi. Như vậy, khối "hoàn thành" của bạn sẽ bắt đầu ngay khi khối đầu tiên báo hiệu semaphore, nhưng trước khi khối khác kết thúc; 2. Cho rằng đây là một câu hỏi iOS, tôi không khuyến khích việc sử dụng dispatch_main.
Cướp

1
Tôi đồng ý với Rob. Đây không phải là một giải pháp hợp lệ. Các dispatch_semaphore_waitsẽ mở khóa càng sớm càng một trong các dispatch_semaphore_signalphương pháp được gọi là. Lý do điều này có thể hoạt động là vì các printfkhối 'một' và 'hai' xảy ra ngay lập tức và printf'cuối cùng' xảy ra sau một thời gian chờ đợi - do đó, sau khi khối một đã ngủ trong 2 giây. Nếu bạn đặt printf sau các sleepcuộc gọi, bạn sẽ nhận được đầu ra cho 'một', sau đó 2 giây cho 'cuối cùng', sau đó 2 giây cho 'hai'.
ɲeuroburɳ

1

Câu trả lời được chấp nhận nhanh chóng:

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

Ví dụ 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()gây ra tai nạn
Ben

-3

Không nói các câu trả lời khác không tuyệt vời cho một số trường hợp nhất định, nhưng đây là một đoạn tôi luôn sử dụng từ Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


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

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.