รูปแบบการทำงานของ Binder ได้รับการออกแบบมาเพื่ออำนวยความสะดวกในการเรียกฟังก์ชันในเครื่อง แม้ว่าการเรียกเหล่านั้นอาจเป็นการเรียกไปยังกระบวนการระยะไกลก็ตาม กล่าวคือ กระบวนการใดๆ ที่โฮสต์โหนดต้องมีพูลของเธรด Binder อย่างน้อย 1 รายการเพื่อจัดการธุรกรรมไปยังโหนดที่โฮสต์ในกระบวนการนั้น
ธุรกรรมแบบซิงโครนัสและแบบอะซิงโครนัส
Binder รองรับธุรกรรมแบบซิงโครนัสและอะซิงโครนัส ส่วนต่อไปนี้จะอธิบายวิธีดำเนินการธุรกรรมแต่ละประเภท
ธุรกรรมแบบซิงโครนัส
ธุรกรรมแบบซิงโครนัสจะบล็อกจนกว่าจะมีการดำเนินการในโหนด และผู้เรียกจะได้รับ การตอบกลับสำหรับธุรกรรมนั้น รูปต่อไปนี้ แสดงวิธีดำเนินการธุรกรรมแบบซิงโครนัส
รูปที่ 1 ธุรกรรมแบบซิงโครนัส
หากต้องการดำเนินการธุรกรรมแบบซิงโครนัส Binder จะทำสิ่งต่อไปนี้
- เธรดใน Threadpool เป้าหมาย (T2 และ T3) จะเรียกใช้เคอร์เนล ไดรเวอร์เพื่อรอการทำงานที่เข้ามา
- เคอร์เนลจะได้รับการทำธุรกรรมใหม่และปลุกเธรด (T2) ใน กระบวนการเป้าหมายเพื่อจัดการธุรกรรม
- เธรดการเรียก (T1) จะบล็อกและรอการตอบกลับ
- กระบวนการเป้าหมายจะดำเนินการธุรกรรมและส่งคืนการตอบกลับ
- เธรดในกระบวนการเป้าหมาย (T2) จะเรียกกลับไปที่ไดรเวอร์เคอร์เนลเพื่อ รอการทำงานใหม่
ธุรกรรมแบบอะซิงโครนัส
ธุรกรรมแบบอะซิงโครนัสจะไม่บล็อกการดำเนินการให้เสร็จสมบูรณ์ แต่จะ ยกเลิกการบล็อกเธรดที่เรียกใช้ทันทีที่ส่งธุรกรรมไปยังเคอร์เนล รูปต่อไปนี้แสดงวิธีดำเนินการธุรกรรมแบบไม่พร้อมกัน
รูปที่ 2 ธุรกรรมแบบอะซิงโครนัส
- เธรดใน Threadpool เป้าหมาย (T2 และ T3) จะเรียกใช้เคอร์เนล ไดรเวอร์เพื่อรอการทำงานที่เข้ามา
- เคอร์เนลจะได้รับการทำธุรกรรมใหม่และปลุกเธรด (T2) ใน กระบวนการเป้าหมายเพื่อจัดการธุรกรรม
- เธรดการเรียก (T1) จะดำเนินการต่อไป
- กระบวนการเป้าหมายจะดำเนินการธุรกรรมและส่งคืนการตอบกลับ
- เธรดในกระบวนการเป้าหมาย (T2) จะเรียกกลับไปที่ไดรเวอร์เคอร์เนลเพื่อ รอการทำงานใหม่
ระบุฟังก์ชันแบบซิงโครนัสหรืออะซิงโครนัส
ฟังก์ชันที่ทำเครื่องหมายเป็น oneway
ในไฟล์ AIDL จะเป็นแบบไม่พร้อมกัน เช่น
oneway void someCall();
หากไม่ได้ทำเครื่องหมายฟังก์ชันเป็น oneway
ฟังก์ชันนั้นจะเป็นฟังก์ชันแบบซิงโครนัส แม้ว่าฟังก์ชันจะแสดงผล void
ก็ตาม
การทำให้ธุรกรรมแบบอะซิงโครนัสเป็นแบบอนุกรม
Binder จะทำการซีเรียลไลซ์ธุรกรรมแบบไม่พร้อมกันจากโหนดเดียว รูปภาพต่อไปนี้แสดงวิธีที่ Binder ทำให้ธุรกรรมแบบอะซิงโครนัสเป็นอนุกรม
รูปที่ 3 การทำให้ธุรกรรมแบบอะซิงโครนัสเป็นแบบอนุกรม
- เธรดใน Threadpool เป้าหมาย (B1 และ B2) จะเรียกใช้เคอร์เนล ไดรเวอร์เพื่อรอการทำงานที่เข้ามา
- ระบบจะส่งธุรกรรม 2 รายการ (T1 และ T2) ในโหนดเดียวกัน (N1) ไปยังเคอร์เนล
- เคอร์เนลจะได้รับธุรกรรมใหม่และจัดลำดับธุรกรรมเหล่านั้นเนื่องจากมาจากโหนดเดียวกัน (N1)
- ระบบจะส่งธุรกรรมอื่นในโหนดอื่น (N2) ไปยังเคอร์เนล
- เคอร์เนลจะได้รับธุรกรรมที่สามและปลุกเธรด (B2) ใน กระบวนการเป้าหมายเพื่อจัดการธุรกรรม
- กระบวนการเป้าหมายจะดำเนินการธุรกรรมแต่ละรายการและส่งคืนการตอบกลับ
ธุรกรรมที่ซ้อนกัน
ธุรกรรมแบบซิงโครนัสสามารถซ้อนกันได้ โดยเธรดที่จัดการธุรกรรมจะออกธุรกรรมใหม่ได้ ธุรกรรมที่ซ้อนกันอาจเป็นธุรกรรมที่ส่งไปยัง กระบวนการอื่น หรือกระบวนการเดียวกันกับที่คุณได้รับธุรกรรมปัจจุบัน ลักษณะการทำงานนี้เลียนแบบการเรียกใช้ฟังก์ชันในเครื่อง ตัวอย่างเช่น สมมติว่าคุณมีฟังก์ชันที่มีฟังก์ชันซ้อนกันดังนี้
def outer_function(x):
def inner_function(y):
def inner_inner_function(z):
หากเป็นการเรียกใช้ในเครื่อง ระบบจะดำเนินการในเธรดเดียวกัน
กล่าวคือ หากผู้เรียกของ inner_function
เป็นกระบวนการ
ที่โฮสต์โหนดที่ใช้ inner_inner_function
ด้วย การเรียกไปยัง
inner_inner_function
จะดำเนินการในเธรดเดียวกัน
รูปภาพต่อไปนี้แสดงวิธีที่ Binder จัดการธุรกรรมที่ซ้อนกัน
รูปที่ 4 ธุรกรรมที่ซ้อนกัน
- เทรด A1 ขอให้เรียกใช้
foo()
- ในส่วนของคำขอนี้ เทรด B1 จะเรียกใช้
bar()
ซึ่ง A จะเรียกใช้ในเทรด A1 เดียวกัน
รูปต่อไปนี้แสดงการดำเนินการของเธรดหากโหนดที่ใช้
bar()
อยู่ในกระบวนการอื่น
รูปที่ 5 ธุรกรรมที่ซ้อนกันในกระบวนการต่างๆ
- เทรด A1 ขอให้เรียกใช้
foo()
- ในส่วนของคำขอนี้ เทรด B1 จะเรียกใช้
bar()
ซึ่งทำงานในเทรด C1 อีกเทรดหนึ่ง
รูปต่อไปนี้แสดงวิธีที่เธรดนำกระบวนการเดียวกันกลับมาใช้ซ้ำได้ทุกที่ใน ห่วงโซ่ธุรกรรม
รูปที่ 6 ธุรกรรมที่ซ้อนกันซึ่งใช้เธรดซ้ำ
- กระบวนการ A โทรหากระบวนการ B
- กระบวนการ B เรียกใช้กระบวนการ C
- จากนั้นกระบวนการ C จะโทรกลับไปยังกระบวนการ A และเคอร์เนล จะใช้เธรด A1 ในกระบวนการ A ซึ่งเป็นส่วนหนึ่งของห่วงโซ่ธุรกรรมซ้ำ
สำหรับการทำธุรกรรมแบบไม่พร้อมกัน การซ้อนจะไม่เกี่ยวข้อง เนื่องจากไคลเอ็นต์ไม่ต้องรอผลลัพธ์ของธุรกรรมแบบไม่พร้อมกัน จึงไม่มีการซ้อน หากตัวแฮนเดิลของธุรกรรมแบบอะซิงโครนัสเรียกใช้กระบวนการที่ออกธุรกรรมแบบอะซิงโครนัส ธุรกรรมนั้นจะได้รับการจัดการในเธรดว่างในกระบวนการนั้น
หลีกเลี่ยงการเกิดภาวะหยุดชะงัก
รูปภาพต่อไปนี้แสดงการเกิดภาวะหยุดชะงักที่พบบ่อย
รูปที่ 7 ภาวะหยุดชะงักที่พบบ่อย
- กระบวนการ A ใช้ Mutex MA และทำการเรียก Binder (T1) ไปยัง กระบวนการ B ซึ่งพยายามใช้ Mutex MB ด้วย
- ในขณะเดียวกัน กระบวนการ B จะใช้ Mutex MB และทำการเรียกใช้ Binder (T2) ไปยัง กระบวนการ A ซึ่งพยายามใช้ Mutex MA
หากธุรกรรมเหล่านี้ทับซ้อนกัน ธุรกรรมแต่ละรายการอาจใช้ mutex ในกระบวนการของตนเองขณะรอให้กระบวนการอื่นปล่อย mutex ซึ่งส่งผลให้เกิดการหยุดชะงัก
หากต้องการหลีกเลี่ยงการหยุดชะงักขณะใช้ Binder อย่าถือล็อกใดๆ เมื่อทำการเรียก Binder
กฎการเรียงลำดับการล็อกและภาวะหยุดชะงัก
ภายในสภาพแวดล้อมการดำเนินการเดียว มักจะหลีกเลี่ยงการเกิดภาวะหยุดชะงักด้วย กฎการจัดลำดับการล็อก อย่างไรก็ตาม เมื่อทำการเรียกใช้ระหว่างกระบวนการและระหว่างโค้ดเบส โดยเฉพาะอย่างยิ่งเมื่อมีการอัปเดตโค้ด การรักษากฎการเรียงลำดับและการประสานงานจึงเป็นไปไม่ได้
Mutex เดียวและการเกิด Deadlock
เมื่อใช้ธุรกรรมที่ซ้อนกัน กระบวนการ B จะเรียกกลับไปยังเธรดเดียวกันในกระบวนการ A ที่ถือ Mutex ได้โดยตรง ดังนั้น การเรียกซ้ำที่ไม่คาดคิดอาจทำให้เกิดภาวะหยุดชะงักด้วย Mutex เดียวได้
การเรียกแบบซิงโครนัสและภาวะหยุดชะงัก
แม้ว่าการเรียก Binder แบบไม่พร้อมกันจะไม่บล็อกเพื่อให้เสร็จสมบูรณ์ แต่คุณก็ควร หลีกเลี่ยงการล็อกสำหรับการเรียกแบบไม่พร้อมกันด้วย หากคุณล็อกไว้ คุณอาจ พบปัญหาเกี่ยวกับการล็อกหากมีการเปลี่ยนการโทรแบบทางเดียวเป็นการโทรแบบ ซิงโครนัสโดยไม่ตั้งใจ
เธรด Binder เดียวและภาวะหยุดชะงัก
โมเดลธุรกรรมของ Binder อนุญาตให้มีการเรียกซ้ำ ดังนั้นแม้ว่ากระบวนการจะมี เธรด Binder เดียว คุณก็ยังต้องใช้การล็อก เช่น สมมติว่าคุณกำลัง วนซ้ำในรายการในกระบวนการแบบเธรดเดียว A สำหรับแต่ละรายการใน list คุณจะทำธุรกรรมการโอน หากการใช้งานฟังก์ชันที่คุณเรียกใช้สร้างธุรกรรม Binder ใหม่ไปยังโหนดที่โฮสต์ในกระบวนการ A ระบบจะจัดการธุรกรรมนั้นในเธรดเดียวกันกับที่วนซ้ำรายการ หากการติดตั้งใช้งานธุรกรรมนั้นแก้ไขรายการเดียวกัน คุณอาจพบปัญหาเมื่อทำซ้ำรายการในภายหลัง
กำหนดค่าขนาด Threadpool
เมื่อบริการมีไคลเอ็นต์หลายราย การเพิ่มเธรดลงใน Threadpool จะช่วยลดการแย่งชิงและให้บริการการเรียกแบบขนานได้มากขึ้น หลังจากจัดการ การทำงานพร้อมกันอย่างถูกต้องแล้ว คุณจะเพิ่มเธรดได้ ปัญหาที่อาจเกิดจากการเพิ่มเธรดจำนวนมาก ซึ่งอาจทำให้ไม่ได้ใช้เธรดบางรายการในระหว่างปริมาณงานที่เงียบ
ระบบจะสร้างเธรดตามต้องการจนกว่าจะถึงจำนวนสูงสุดที่กำหนดค่าไว้ หลังจากสร้างเธรด Binder แล้ว เธรดจะยังคงทำงานอยู่จนกว่ากระบวนการที่โฮสต์เธรดจะสิ้นสุดลง
ไลบรารี libbinder มีค่าเริ่มต้นเป็น 15 เธรด ใช้
setThreadPoolMaxThreadCount
เพื่อเปลี่ยนค่านี้
using ::android::ProcessState;
ProcessState::self()->setThreadPoolMaxThreadCount(size_t maxThreads);