iPhone - Grand Central Dispatch หัวข้อหลัก


145

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

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

หรือแม้กระทั่ง

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

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

[self doStuff];

ขวา?

ฉันสงสัยว่าพวกคุณคิดอย่างไร


9
โดยวิธีการโยนคิวหลักลงใน dispatch_sync จะส่งผลให้เกิดการหยุดชะงัก
Brooks Hanes

5
เพียงอ่านในเอกสาร: "ไม่เหมือน dispatch_async [dispatch_sync] จะไม่กลับมาจนกว่าบล็อกจะเสร็จสิ้นการเรียกใช้ฟังก์ชันนี้และกำหนดเป้าหมายคิวปัจจุบันทำให้เกิดการหยุดชะงัก" ... แต่บางทีฉันอาจอ่านผิด ... ( คิวปัจจุบันไม่ได้หมายถึงเธรดหลัก) โปรดแก้ไขหากฉันผิด
Brooks Hanes

4
@BrooksHanes ไม่จริงเสมอไป จะทำให้เกิดการหยุดชะงักหากคุณอยู่ในเธรดหลักแล้ว ถ้าไม่เช่นนั้นจะไม่มีการหยุดชะงัก ดูที่นี่
น้ำผึ้ง

คำตอบ:


296

การส่งบล็อคไปยังคิวหลักมักจะทำจากคิวพื้นหลังเพื่อส่งสัญญาณว่าการประมวลผลเบื้องหลังบางส่วนเสร็จสิ้นเช่น

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

ในกรณีนี้เราทำการคำนวณที่มีความยาวบนคิวแบ็คกราวน์และจำเป็นต้องอัปเดต UI ของเราเมื่อการคำนวณเสร็จสมบูรณ์ โดยปกติแล้วการอัปเดต UI จะต้องทำจากคิวหลักดังนั้นเราจึง 'ส่งสัญญาณ' กลับไปที่คิวหลักโดยใช้ dispatch_async ที่ซ้อนกันอันดับสอง

อาจมีตัวอย่างอื่นที่คุณอาจต้องการส่งกลับไปที่คิวหลัก แต่โดยทั่วไปแล้วจะทำในลักษณะนี้เช่นซ้อนกันจากภายในบล็อกที่ส่งไปยังคิวพื้นหลัง

  • การประมวลผลเบื้องหลังเสร็จสิ้น -> อัปเดต UI
  • กลุ่มข้อมูลที่ประมวลผลบนคิวพื้นหลัง -> ส่งสัญญาณคิวหลักเพื่อเริ่มกลุ่มถัดไป
  • ข้อมูลเครือข่ายขาเข้าในคิวพื้นหลัง -> ส่งสัญญาณคิวหลักที่ข้อความมาถึง
  • ฯลฯ

สำหรับสาเหตุที่คุณอาจต้องการส่งไปยังคิวหลักจากคิวหลัก ... โดยทั่วไปคุณจะไม่คิดว่าคุณอาจทำมันเพื่อกำหนดเวลางานบางอย่างที่จะทำในครั้งต่อไปรอบลูปรัน


อ่าฉันเข้าใจแล้ว ดังนั้นฉันถูก ไม่มีประโยชน์ในการทำเช่นนั้นหากคุณอยู่ในคิวหลักอยู่แล้วหากคุณอยู่ในคิวอื่นและต้องการอัปเดต UI ขอบคุณ
Duck

เพิ่งแก้ไขคำตอบของฉันเพื่อพูดคุยเกี่ยวกับสาเหตุที่ไม่มีประโยชน์มากในการทำเช่นนี้จากคิวหลัก
Robin Summerhill

นอกจากนี้ฉันคิดว่ามีข้อผิดพลาดใน iOS 4 (อาจหายไปใน iOS 5) ที่ dispatch_sync ไปยังคิวหลักจากเธรดหลักเพียงทำให้แฮงค์ดังนั้นฉันจะหลีกเลี่ยงการทำเช่นนั้นโดยสิ้นเชิง
joerick

10
นั่นไม่ใช่ข้อผิดพลาดนั่นเป็นพฤติกรรมที่คาดหวัง พฤติกรรมไม่เป็นประโยชน์มากนัก แต่คุณต้องระวังการหยุดชะงักเมื่อใช้ dispatch_sync คุณไม่สามารถคาดหวังได้ว่าระบบจะป้องกันคุณจากข้อผิดพลาดของโปรแกรมเมอร์ตลอดเวลา
Robin Summerhill

2
BackgroundQueue ที่นี่คืออะไร ฉันจะสร้างวัตถุ backgroundQueue ได้อย่างไร
Nilesh Tupe

16

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

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

หากโปรแกรมของคุณไม่ทำอะไรเลยนอกจากใช้เวลาทั้งชีวิตในการตอบสนองต่อเหตุการณ์ต่าง ๆ สิ่งนี้อาจเป็นเรื่องธรรมชาติ คุณเพิ่งตั้งค่าตัวจัดการเหตุการณ์ให้ทำงานบนคิวหลักจากนั้นเรียก dispatch_main () และคุณอาจไม่ต้องกังวลเกี่ยวกับความปลอดภัยของเธรดเลย


11

หวังว่าฉันเข้าใจคำถามของคุณอย่างถูกต้องในสิ่งที่คุณสงสัยเกี่ยวกับความแตกต่างระหว่าง dispatch_async และ dispatch_sync?

dispatch_async

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

dispatch_sync

จะส่งบล็อคไปยังคิวพร้อมกัน สิ่งนี้จะป้องกันการเรียกใช้โค้ดที่เหลืออีกในเมธอดจนกว่าบล็อกจะดำเนินการเสร็จสิ้น

ฉันส่วนใหญ่ใช้ a dispatch_asyncเป็นคิวเบื้องหลังเพื่อทำงานนอกคิวหลักและใช้ประโยชน์จากคอร์เพิ่มเติมใด ๆ ที่อุปกรณ์อาจมี ถ้าอย่างนั้นdispatch_asyncฉันต้องอัพเดต UI

โชคดี


1
ขอบคุณ แต่ฉันกำลังถามถึงข้อดีจากการส่งบางสิ่งไปยังคิวหลักอยู่ในคิวหลัก
Duck

9

ที่เดียวที่เป็นประโยชน์สำหรับกิจกรรม UI เช่นการตั้งค่าสปินเนอร์ก่อนการดำเนินการที่ยาวนาน:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

จะไม่ทำงานเพราะคุณกำลังปิดกั้นเธรดหลักในระหว่างที่คุณอยู่ในระยะยาวและไม่อนุญาตให้ UIKit เริ่มปั่น

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

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


@Jereratops ใช่ แต่อนุญาตให้ runloop ปัจจุบันเสร็จสมบูรณ์
Dan Rosenstark

3
ใช่ แต่มันก็แย่มาก มันยังบล็อก UI ฉันอาจกดปุ่มอื่นหลังจากนี้ หรือลองแล้วเลื่อน "(ทำบางสิ่งที่มีความยาว)" ไม่ควรเกิดขึ้นในเธรดหลักและ dispatch_async เพื่อให้การคลิกปุ่ม "เสร็จสิ้น" ไม่ใช่วิธีการแก้ปัญหาที่ยอมรับได้
Jerceratops


1

Async หมายถึงอะซิงโครนัสและคุณควรใช้เวลาส่วนใหญ่ คุณไม่ควรเรียกการซิงค์บนเธรดหลักเพราะมันจะล็อคค่า UI ของคุณจนกว่างานจะเสร็จสมบูรณ์ คุณอยู่ที่นี่เป็นวิธีที่ดีกว่าในการทำสิ่งนี้ใน Swift:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

มันรวมเป็นฟังก์ชั่นมาตรฐานใน repo ของฉันลองดู: https://github.com/goktugyil/EZSwiftExtensions

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