รอจนกระทั่งมีการเรียกใช้บล็อก async สองบล็อกก่อนเริ่มบล็อกอื่น


192

เมื่อใช้ GCD เราต้องการรอจนกว่าจะมีการเรียกใช้และบล็อก async สองบล็อกก่อนที่จะดำเนินการขั้นตอนถัดไป วิธีที่ดีที่สุดในการทำเช่นนั้นคืออะไร?

เราลองทำสิ่งต่อไปนี้ แต่ดูเหมือนจะไม่ทำงาน:

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

ดูคำตอบของฉันสำหรับ Swift 5 ที่เสนอวิธีการต่าง ๆ ถึงหกวิธีในการแก้ปัญหาของคุณ
Imanou Petit

คำตอบ:


301

ใช้กลุ่มการแจกจ่าย: ดูที่นี่สำหรับตัวอย่าง "การรอคอยในกลุ่มงานที่ถูกจัดคิว" ในบท "การจัดส่งคิว" ของคู่มือการเขียนโปรแกรมพร้อมกันของไลบรารีนักพัฒนา iOS ของ Apple ของ 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

3
เย็น. ภารกิจ / บล็อกของ async จะเชื่อมโยงกับกลุ่มเมื่อดำเนินการตามลำดับหรือพร้อมกันหรือไม่ ฉันหมายความว่าสมมติว่า block1 และ block2 เชื่อมโยงกับกลุ่มในขณะนี้ block2 จะรอจนกว่าจะเสร็จสิ้นการ block1 ก่อนจึงจะเริ่มดำเนินการได้
ทอม

9
ขึ้นอยู่กับคุณ dispatch_group_asyncเหมือนกับdispatch_asyncการเพิ่มพารามิเตอร์กลุ่ม ดังนั้นหากคุณใช้คิวที่แตกต่างกันสำหรับ block1 และ block2 หรือกำหนดเวลาไว้ในคิวเดียวกันพร้อมกันพวกเขาสามารถทำงานพร้อมกันได้ หากคุณกำหนดเวลาไว้ในคิวอนุกรมเดียวกันพวกเขาจะทำงานตามลำดับ ไม่ต่างจากการตั้งเวลาบล็อกโดยไม่มีกลุ่ม
Jörn Eyrich

1
สิ่งนี้ใช้กับการดำเนินการโพสต์บริการเว็บหรือไม่?
SleepNot

คุณสังเกตเห็นว่าเวลาไม่เท่ากับเวลานอนหลับที่ตั้งไว้ในบล็อกของคุณ? ทำไมถึงเป็นเช่นนี้
Damon Yuan

2
ใน ARC เพียงลบ dispatch_release (กลุ่ม);
loretoparisi

272

ขยายคำตอบของJörn Eyrich (ขึ้นอยู่กับคำตอบของเขาถ้าคุณ upvote นี้) หากคุณไม่สามารถควบคุมการdispatch_asyncโทรสำหรับบล็อกของคุณได้เช่นในกรณีที่บล็อก async เสร็จสมบูรณ์คุณสามารถใช้กลุ่ม GCD โดยใช้dispatch_group_enterและdispatch_group_leaveโดยตรง

ในตัวอย่างนี้เราแสร้งทำเป็นcomputeInBackgroundสิ่งที่เราไม่สามารถเปลี่ยนแปลงได้ (ลองนึกภาพว่ามันคือการติดต่อกลับของผู้มอบหมาย NSURLC การเชื่อมต่อเสร็จสิ้นแฮนเดอร์หรืออะไรก็ตาม) และดังนั้นเราจึงไม่สามารถเข้าถึงการส่งได้

// 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: complete: ถูกนำมาใช้เป็น:

- (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
@cartart จับดี! ฉันได้อัปเดตโค้ดตัวอย่างเพื่อแสดงความคิดเห็นของคุณแล้ว หลายครั้งที่คุณต้องการให้การติดต่อกลับอยู่ในคิวหลัก - ในกรณีนั้นแม้ว่าdispatch_queue_notifyจะดีกว่า (เว้นแต่ว่าเวลาการบล็อกจะสั้น)
uroeuroburɳ

ฉันจะปล่อยกลุ่ม (เช่น dispatch_release (กลุ่ม)) ได้ที่ไหน ฉันไม่แน่ใจว่าปลอดภัยที่จะเผยแพร่ใน 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เสนอวิธีแก้ปัญหาของคุณได้หลายวิธี ตามความต้องการของคุณคุณสามารถเลือกหนึ่งในเจ็ดรูปแบบที่แสดงในตัวอย่างสนามเด็กเล่นต่อไปนี้


# 1 ใช้DispatchGroup, DispatchGroup's notify(qos:flags:queue:execute:)และDispatchQueue' sasync(group:qos:flags:execute:)

คู่มือการเขียนโปรแกรมพร้อมกันของ Developer Developer ของ 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, DispatchGroup's wait(), DispatchGroup' s 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
 */

โปรดทราบว่าคุณยังสามารถผสมDispatchGroup wait()กับDispatchQueue async(group:qos:flags:execute:)หรือผสมDispatchGroup enter()และมีDispatchGroup leave()DispatchGroup notify(qos:flags:queue:execute:)


# 3 การใช้และ'sDispatch​Work​Item​Flags barrierDispatchQueueasync(group:qos:flags:execute:)

Grand Central Dispatch Tutorial สำหรับ Swift 4: Part 1/2บทความจาก Raywenderlich.com ให้คำจำกัดความสำหรับอุปสรรค :

Dispatch barrier เป็นกลุ่มของฟังก์ชันที่ทำหน้าที่เป็นคอขวดแบบอนุกรมเมื่อทำงานกับคิวที่เกิดขึ้นพร้อมกัน เมื่อคุณส่ง a 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's barrierและ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 ใช้DispatchSemaphore, DispatchSemaphore's wait()และDispatchSemaphore' ssignal()

Soroush Khanlou เขียนบรรทัดต่อไปนี้ในโพสต์บล็อกThe GCD Handbook :

ใช้เซมาฟอร์เราสามารถบล็อกเธรดได้ตามระยะเวลาที่กำหนดจนกว่าจะมีการส่งสัญญาณจากเธรดอื่น Semaphores เช่น GCD ส่วนที่เหลือนั้นมีความปลอดภัยต่อเธรดและสามารถถูกเรียกใช้จากที่ใดก็ได้ สามารถใช้เซมิโคลอนเมื่อมี API แบบอะซิงโครนัสที่คุณต้องทำการซิงโครนัส แต่คุณไม่สามารถแก้ไขได้

การอ้างอิง API สำหรับนักพัฒนาของ Apple ยังให้การอภิปรายต่อไปนี้สำหรับเครื่องมือDispatchSemaphore init(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และOperation'saddDependency(_:)

การอ้างอิง API สำหรับนักพัฒนาของ Apple เกี่ยวกับ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และOperationQueues' 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
 */

มีวิธีแก้ปัญหาสำหรับการโทรแบบ async โดยไม่ใช้ group.enter () และ group.leave () สำหรับแต่ละคน (และไม่มีเซมาฟอร์) หรือไม่? เช่นถ้าฉันต้องรอคำขอ async ไปยังเซิร์ฟเวอร์แล้วหลังจากนั้นรอคำขอ async ที่สองเป็นต้น ฉันได้อ่านบทความนี้avanderlee.com/swift/asynchronous-operationsแต่ฉันไม่มาดูการใช้งานที่เรียบง่ายของมันเมื่อเทียบกับ BlockOperation
โฮ่ง

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

เพียงแค่สร้างคิวพร้อมกันส่งสองบล็อกของคุณแล้วส่งบล็อกสุดท้ายพร้อมกับสิ่งกีดขวางซึ่งจะทำให้รออีกสองเสร็จสิ้น


มีปัญหาหรือไม่ถ้าฉันไม่ได้ใช้โหมดสลีป (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 ของคุณซิงโครนัส แต่ถ้ามันไม่ได้และคุณต้องการที่จะทริกเกอร์เสร็จสิ้นเมื่อการดำเนินการ async ของคุณจะทำอย่างไร
เกร็ก 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 - หากคุณต้องการให้งานหนึ่งเสร็จก่อนที่งานสองงานจะเริ่มให้เพิ่มการพึ่งพาระหว่างงานเหล่านั้น หรือถ้าคิวอยู่เสมอเพียงการปฏิบัติงานในช่วงเวลาหนึ่งทำให้มันเป็นคิวอนุกรมโดยการตั้งค่าไปmaxConcurrentOperationCount 1คุณสามารถกำหนดลำดับความสำคัญของการดำเนินการได้เช่นกันqualityOfServiceและqueuePriorityแต่สิ่งเหล่านี้มีผลกระทบที่ละเอียดอ่อนกว่าต่อลำดับความสำคัญของงานมากกว่าการอ้างอิงและ / หรือระดับของการทำงานพร้อมกันของคิว
Rob

4

คำตอบข้างต้นล้วน แต่เท่ห์ แต่พวกเขาก็พลาดสิ่งหนึ่งไป งานรันกลุ่ม (บล็อค) ในหัวข้อที่มันเข้ามาเมื่อคุณใช้/dispatch_group_enterdispatch_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

คำตอบแรกนั้นถูกต้องเป็นหลัก แต่ถ้าคุณต้องการวิธีที่ง่ายที่สุดในการบรรลุผลตามที่ต้องการนี่คือตัวอย่างของรหัสแบบสแตนด์อโลนที่แสดงให้เห็นว่าจะทำอย่างไรกับเซมาฟอร์ :

#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วิธีการที่เรียกว่า สาเหตุที่อาจดูเหมือนว่าทำงานได้เนื่องจากมีการprintfบล็อก 'หนึ่ง' และ 'สอง' เกิดขึ้นทันทีและprintfสำหรับ 'สุดท้าย' เกิดขึ้นหลังจากรอ - ดังนั้นหลังจากบล็อกหนึ่งหลับไป 2 วินาที หากคุณวาง printf หลังการsleepโทรคุณจะได้ผลลัพธ์เป็น 'หนึ่ง' และ 2 วินาทีต่อมาสำหรับ 'สุดท้าย' จากนั้น 2 วินาทีต่อมาสำหรับ 'สอง'
uroeuroburɳ

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

ตัวอย่าง 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()ทำให้เกิดข้อผิดพลาด
เบ็น

-3

ไม่พูดคำตอบอื่น ๆ ไม่เหมาะสำหรับบางสถานการณ์ แต่นี่เป็นตัวอย่างข้อมูลที่ฉันมักจะใช้จาก Google:

- (void)runSigninThenInvokeSelector:(SEL)signInDoneSel {


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

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.