ความแตกต่างระหว่าง DispatchQueue.main.async และ DispatchQueue.main.sync


115

ฉันใช้DispatchQueue.main.asyncมานานเพื่อดำเนินการที่เกี่ยวข้องกับ UI



Swift จัดเตรียมทั้งDispatchQueue.main.asyncและและDispatchQueue.main.syncและทั้งสองอย่างจะดำเนินการในคิวหลัก



ใครช่วยบอกความแตกต่างระหว่างพวกเขาได้ไหม ฉันควรใช้แต่ละครั้งเมื่อใด



DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}

คำตอบ:


61

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

ใช้เมื่อไหร่sync? เมื่อคุณต้องการรอบางสิ่งที่ทำในคิวที่แตกต่างกันจากนั้นจึงทำงานต่อในคิวปัจจุบันของคุณ

ตัวอย่างการใช้การซิงค์:

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


การเรียกDispatchQueue.main.syncจากเธรดพื้นหลังจะผิดหรือไม่?
น้ำผึ้ง

@Honey โดยทั่วไปไม่มีไม่มีอะไรผิดปกติกับการโทรแบบนี้ (ตราบใดที่คิวหลักไม่ได้ทำอะไรหนักและเสียเวลา) แต่ในทางปฏิบัติฉันไม่สามารถนึกถึงสถานการณ์ที่คุณต้องการสิ่งนี้จริงๆ ควรมีทางออกที่ดีกว่านี้แน่นอน
Andrey Chernukha

1
@Honey One สถานการณ์ดังกล่าวกำลังอัปเดต CollectionView ของ PHAssets จาก PhotoKit API ตามที่แสดงในเอกสารที่นี่: developer.apple.com/documentation/photokit/…
teacup

1
@teacup น่าสนใจ. ฉันแค่สงสัยว่ามันจะแตกต่างกันอย่างไรถ้าเราโทรไปที่asyncนั่น? ฉันหมายถึงเนื่องจากไม่มีอะไรอื่นในเธรดหลังจากนั้นมันก็ไม่ได้สร้างความแตกต่าง ถ้าเป็นเช่นDispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};นั้นมันจะสมเหตุสมผล แต่เมื่อไม่มีบล็อกอื่น ๆ แล้วฉันไม่สามารถคิดของผลประโยชน์ของการใช้มากกว่าDispatchQueue.main.sync {Oneblock} DispatchQueue.main.async {Oneblock}สำหรับทั้งคู่พวกเขาจะได้รับลำดับความสำคัญของคิว / ความฉับไวเป็นหลักและไม่มีอะไรมาขัดขวางพวกเขาได้
น้ำผึ้ง

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

169

ทำไมต้องทำงานพร้อมกัน

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

มีนามธรรมบางประการที่เราต้องทราบ

  • คิว
  • ประสิทธิภาพของงานซิงโครนัส / อะซิงโครนัส
  • ลำดับความสำคัญ
  • ปัญหาทั่วไป

คิว

จะต้องเป็นแบบอนุกรมหรือพร้อมกัน ตลอดจนระดับโลกหรือส่วนตัวในเวลาเดียวกัน

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

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

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

คุณภาพการบริการ / ลำดับความสำคัญ

คิวยังมีqos (คุณภาพการบริการ)ที่แตกต่างกันซึ่งตั้งค่างานที่ดำเนินการตามลำดับความสำคัญ (จากสูงสุดไปต่ำสุดที่นี่):
.userInteractive - คิวหลัก .
userInitiated - สำหรับงานที่ผู้ใช้เริ่มต้นซึ่งผู้ใช้รอการตอบสนองบางอย่างที่มี
ประสิทธิภาพ - สำหรับงาน ซึ่งใช้เวลาพอสมควรและไม่ต้องการการตอบสนองในทันทีเช่นการทำงานกับข้อมูล
พื้นหลัง - สำหรับงานที่ไม่เกี่ยวข้องกับส่วนภาพและไม่เข้มงวดสำหรับเวลาที่ทำให้เสร็จสมบูรณ์)

นอกจากนี้ยังมี

.DEFAULTคิวซึ่งการถ่ายโอนกวนQoSข้อมูล ถ้ามันเป็นไปไม่ได้ในการตรวจสอบQoSqosจะถูกใช้ระหว่าง. userInitiatedและ. utility

งานสามารถดำเนินการพร้อมกันหรือไม่พร้อมกัน

  • ฟังก์ชันซิงโครนัสส่งคืนการควบคุมไปยังคิวปัจจุบันหลังจากงานเสร็จสิ้นเท่านั้น มันบล็อกคิวและรอจนกว่างานจะเสร็จสิ้น

  • ฟังก์ชันอะซิงโครนัสส่งคืนการควบคุมไปยังคิวปัจจุบันทันทีหลังจากที่ส่งงานไปดำเนินการกับคิวอื่น ไม่รอจนกว่างานจะเสร็จสิ้น มันไม่ขวางคิว

ปัญหาทั่วไป

โปรแกรมเมอร์ข้อผิดพลาดที่ได้รับความนิยมมากที่สุดในขณะที่ฉายแอพพร้อมกันมีดังต่อไปนี้:

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

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

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


3
ฉันไม่คิดว่า "ไม่เคยเรียกฟังก์ชันการซิงค์บนคิวหลัก" ถูกต้อง มีหลายกรณีที่คุณจะเรียกการซิงค์ในเธรดหลักเช่นเมื่อคุณมีตัวนับส่วนกลางที่คุณต้องการให้แต่ละอ็อบเจ็กต์ใช้และเพิ่ม: dispatchQueue.sync {count + = 1; self.orderId = count}
Elisha Sterngold

6
QOS Class - .userInteractive ไม่ใช่คิวหลัก
Kunal Shah

1
การเรียกDispatchQueue.main.syncจากเธรดพื้นหลังจะผิดหรือไม่?
น้ำผึ้ง

1
@ ฮันนี่ไม่ผิดที่จะเรียกแบบนั้น แต่จากประสบการณ์ของฉันคุณจะพบว่าตัวเองเรียก DispatchQueue.main.async มากกว่าซิงค์
James Kim

3
การบอกว่าคุณไม่ควรเรียกใช้ฟังก์ชัน sync () ในคิวปัจจุบันจะถูกต้องกว่าหรือไม่ ไม่ผิดที่จะเรียก sync () ในคิวหลักถ้าคุณอยู่ในคิวอื่นถ้าฉันเข้าใจถูกต้อง
ykay

2

GCDให้คุณสั่งงานsynchronouslyหรือasynchronously[เกี่ยวกับ]

synchronousฟังก์ชัน (block and wait) ส่งกลับตัวควบคุมเมื่องานจะเสร็จสมบูรณ์

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

[DispatchQueue]


0

syncหรือasyncวิธีการไม่มีผลต่อคิวที่เรียก

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

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

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