GCD เพื่อทำงานในเธรดหลัก


255

ฉันมีการโทรกลับซึ่งอาจมาจากเธรดใด ๆ เมื่อฉันได้รับการติดต่อกลับนี้ฉันต้องการทำงานบางอย่างบนเธรดหลัก

ฉันจำเป็นต้องตรวจสอบว่าฉันอยู่ในหัวข้อหลักแล้วหรือมีบทลงโทษใด ๆ หรือไม่โดยไม่ทำการตรวจสอบนี้ก่อนที่จะเรียกรหัสด้านล่าง

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});

117
ห้าปีต่อมาฉันยังจำไวยากรณ์ของบล็อก GCD ไม่ได้และจบลงที่นี่ทุกครั้ง
SpaceTrucker

7
@SpaceTrucker - ด้วยเหตุผลเดียวกันฉันอยู่ในหน้านี้: D
Matthew Cawley

4
9 ปีต่อมาและฉันยังคงคัดลอกไวยากรณ์จากหน้านี้
Osa

คำตอบ:


153

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

หากคุณอยู่ในเธรดหลักแล้วพฤติกรรมจะเหมือนกัน: บล็อกถูกกำหนดเวลาและดำเนินการเมื่อมีการรันลูปของเธรดหลัก


3
คำถามคือไม่ว่าจะมี "โทษโดยไม่ทำการตรวจสอบนี้" ... ฉันคิดว่ามีการลงโทษประสิทธิภาพการใช้ async ส่งเมื่อมันไม่จำเป็นหรือมันเป็นเรื่องเล็กน้อย?
Dan Rosenstark

1
@ ใช่ฉันไม่คิดว่าจะมีผลกระทบต่อประสิทธิภาพที่เห็นได้ชัดเจนในกรณีส่วนใหญ่: GCD เป็นห้องสมุดที่มีน้ำหนักเบา ที่กล่าวว่าฉันเข้าใจคำถามเป็น: 'ได้รับรหัสด้านล่างฉันต้องตรวจสอบว่าฉันอยู่ในหัวข้อหลัก?'

7
อย่างไรก็ตามคุณจำเป็นต้องตรวจสอบว่าคุณใช้ dispatch_sync หรือไม่ มิฉะนั้นคุณจะได้รับการหยุดชะงัก
Victor Engel

หากคุณอยู่ในคิวและการจัดส่งหลักasyncกลับไปยังคิวหลักก็จะเป็นระยะ แต่ที่ระเบียบพฤษภาคมถึงระยะเวลาที่คาดว่าการกระทำของคุณ เช่นรหัส UI ในviewDidLoad() ไม่ทำงานจนกว่าหลังจากมุมมองจะปรากฏขึ้นเป็นครั้งแรก
pkamb

106

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

อย่างไรก็ตามหากคุณพยายามทำตามข้างบนโดยใช้ a dispatch_sync()และการติดต่อกลับของคุณอยู่ในเธรดหลักแอปพลิเคชันของคุณจะหยุดชะงักที่จุดนั้น ผมอธิบายเรื่องนี้ในคำตอบของฉันที่นี่-performSelectorOnMainThread:เพราะพฤติกรรมนี้ทำให้ผมประหลาดใจเมื่อมีการย้ายโค้ดบางส่วนจาก เมื่อฉันพูดถึงที่นั่นฉันได้สร้างฟังก์ชั่นตัวช่วย:

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

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

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

1
ทำไมไม่เรียกมันว่า "runOnMainQueueSync" ความจริงที่ว่ามันอาจหยุดชะงักและไม่ได้เป็นสิ่งที่ฉันไม่ต้องการมีมากกว่ารหัสของฉัน ขอขอบคุณและ +1 เช่นเคย
Dan Rosenstark

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

3
มันยังคงเป็นไปได้ที่จะหยุดชะงักด้วยฟังก์ชั่นนี้ การซิงค์คิวหลัก> การซิงค์คิวอื่น> เมนคิวจะหยุดชะงัก
hfossli

2
@hfossli - จริงไม่สามารถจัดการได้ทุกกรณี ในการใช้งานของฉันฉันมักจะโทรมาจากการจัดส่งแบบอะซิงโครนัสในคิวอนุกรมพื้นหลังหรือรหัสแบบอินไลน์ในเธรดหลัก ฉันสงสัยว่าdispatch_set_specific()จะช่วยในกรณีที่คุณอธิบาย: stackoverflow.com/a/12806754/19679
แบรดลาร์สัน

1
[NSThread isMainThread] มักจะส่งคืน YES และไม่ถือว่าปลอดภัยสำหรับการตรวจสอบกรณีนี้ในการเขียนโปรแกรม GCD stackoverflow.com/questions/14716334/…
Will Larche

57

ตามคำตอบอื่น ๆ ที่กล่าวถึง dispatch_async จากเธรดหลักนั้นใช้ได้

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

ตัวอย่างเช่น,

NSLog(@"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"inside dispatch async block main thread from main thread");
});
NSLog(@"after dispatch async");

จะพิมพ์ออกมา:

before dispatch async
after dispatch async
inside dispatch async block main thread from main thread

ด้วยเหตุผลนี้หากคุณคาดว่าบล็อกจะดำเนินการระหว่าง NSLog ด้านนอก dispatch_async จะไม่ช่วยคุณ


จำเป็นต้องพูดว่านี่เป็นสิ่งสำคัญที่จะต้องพิจารณา
Marc-Alexandre Bérubé

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

1

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

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

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.