จัดการชุดข้อความ

โมเดลการทำงานแบบมัลติเธรดของ Binder ออกแบบมาเพื่ออำนวยความสะดวกในการเรียกใช้ฟังก์ชันภายใน แม้ว่าการเรียกใช้เหล่านั้นอาจเป็นการเรียกใช้กระบวนการระยะไกลก็ตาม โดยเฉพาะอย่างยิ่ง กระบวนการใดก็ตามที่โฮสต์โหนดจะต้องมีพูลของ Binder Thread อย่างน้อย 1 รายการเพื่อจัดการธุรกรรมกับโหนดที่โฮสต์ในกระบวนการนั้น

ธุรกรรมแบบซิงโครนัสและอะซิงโครนัส

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

ธุรกรรมแบบซิงโครนัส

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

ธุรกรรมแบบซิงโครนัส

รูปที่ 1 ธุรกรรมแบบซิงโครนัส

หากต้องการดำเนินการธุรกรรมแบบซิงโครนัส Binder จะทำดังนี้

  1. เธรดใน Threadpool เป้าหมาย (T2 และ T3) จะเรียกใช้ไดรเวอร์เคอร์เนลเพื่อรอรับงานที่เข้ามา
  2. เคอร์เนลได้รับธุรกรรมใหม่และปลุกเธรด (T2) ในกระบวนการเป้าหมายเพื่อจัดการธุรกรรม
  3. เธรดการเรียก (T1) จะบล็อกและรอคำตอบ
  4. กระบวนการเป้าหมายจะดำเนินการธุรกรรมและแสดงผลคำตอบ
  5. เธรดในกระบวนการเป้าหมาย (T2) จะเรียกใช้ไดรเวอร์เคอร์เนลอีกครั้งเพื่อรอรับงานใหม่

ธุรกรรมแบบอะซิงโครนัส

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

ธุรกรรมแบบอะซิงโครนัส

รูปที่ 2 ธุรกรรมแบบอะซิงโครนัส

  1. เธรดใน Threadpool เป้าหมาย (T2 และ T3) จะเรียกใช้ไดรเวอร์เคอร์เนลเพื่อรอรับงานที่เข้ามา
  2. เคอร์เนลได้รับธุรกรรมใหม่และปลุกเธรด (T2) ในกระบวนการเป้าหมายเพื่อจัดการธุรกรรม
  3. เธรดการเรียก (T1) จะดำเนินการต่อไป
  4. กระบวนการเป้าหมายจะดำเนินการธุรกรรมและแสดงผลคำตอบ
  5. เธรดในกระบวนการเป้าหมาย (T2) จะเรียกใช้ไดรเวอร์เคอร์เนลอีกครั้งเพื่อรอรับงานใหม่

ระบุฟังก์ชันแบบซิงโครนัสหรืออะซิงโครนัส

ฟังก์ชันที่ทำเครื่องหมายเป็น oneway ในไฟล์ AIDL จะเป็นแบบอะซิงโครนัส เช่น

oneway void someCall();

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

การซีเรียลไลซ์ธุรกรรมแบบอะซิงโครนัส

Binder จะซีเรียลไลซ์ธุรกรรมแบบอะซิงโครนัสจากโหนดเดียว รูปต่อไปนี้แสดงวิธีที่ Binder ซีเรียลไลซ์ธุรกรรมแบบอะซิงโครนัส

การทำให้ธุรกรรมแบบอะซิงโครนัสเป็นแบบอนุกรม

รูปที่ 3 การซีเรียลไลซ์ธุรกรรมแบบอะซิงโครนัส

  1. เธรดใน Threadpool เป้าหมาย (B1 และ B2) จะเรียกใช้ไดรเวอร์เคอร์เนลเพื่อรอรับงานที่เข้ามา
  2. ระบบจะส่งธุรกรรม 2 รายการ (T1 และ T2) ในโหนดเดียวกัน (N1) ไปยังเคอร์เนล
  3. เคอร์เนลได้รับธุรกรรมใหม่และซีเรียลไลซ์ธุรกรรมเหล่านั้นเนื่องจากมาจากโหนดเดียวกัน (N1)
  4. ระบบจะส่งธุรกรรมอื่นในโหนดอื่น (N2) ไปยังเคอร์เนล
  5. เคอร์เนลได้รับธุรกรรมที่ 3 และปลุกเธรด (B2) ในกระบวนการเป้าหมายเพื่อจัดการธุรกรรม
  6. กระบวนการเป้าหมายจะดำเนินการธุรกรรมแต่ละรายการและแสดงผลคำตอบ

ธุรกรรมแบบซ้อน

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

def outer_function(x):
    def inner_function(y):
        def inner_inner_function(z):

หากเป็นการเรียกใช้ภายใน ระบบจะดำเนินการในเธรดเดียวกัน โดยเฉพาะอย่างยิ่ง หากผู้เรียก inner_function เป็นกระบวนการ ที่โฮสต์โหนดที่ใช้ inner_inner_function การเรียกใช้ inner_inner_function จะดำเนินการในเธรดเดียวกัน

รูปต่อไปนี้แสดงวิธีที่ Binder จัดการธุรกรรมแบบซ้อน

ธุรกรรมที่ซ้อนกัน

รูปที่ 4 ธุรกรรมแบบซ้อน

  1. เธรด A1 ขอให้เรียกใช้ foo()
  2. เธรด B1 จะเรียกใช้ bar() ซึ่ง A จะเรียกใช้ในเธรด A1 เดียวกัน ซึ่งเป็นส่วนหนึ่งของคำขอนี้

รูปต่อไปนี้แสดงการดำเนินการเธรดหากโหนดที่ใช้ bar() อยู่ในกระบวนการอื่น

ธุรกรรมที่ซ้อนกันในกระบวนการต่างๆ

รูปที่ 5 ธุรกรรมแบบซ้อนในกระบวนการต่างๆ

  1. เธรด A1 ขอให้เรียกใช้ foo()
  2. เธรด B1 จะเรียกใช้ bar() ซึ่งจะเรียกใช้ในเธรด C1 อื่น ซึ่งเป็นส่วนหนึ่งของคำขอนี้

รูปต่อไปนี้แสดงวิธีที่เธรดใช้กระบวนการเดียวกันซ้ำในห่วงโซ่ธุรกรรม

ธุรกรรมที่ซ้อนกันซึ่งใช้เธรดซ้ำ

รูปที่ 6 ธุรกรรมแบบซ้อนที่ใช้เธรดซ้ำ

  1. กระบวนการ A เรียกใช้กระบวนการ B
  2. กระบวนการ B เรียกใช้กระบวนการ C
  3. จากนั้นกระบวนการ C จะเรียกใช้กระบวนการ A อีกครั้ง และเคอร์เนลจะใช้เธรด A1 ในกระบวนการ A ซ้ำ ซึ่งเป็นส่วนหนึ่งของห่วงโซ่ธุรกรรม

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

หลีกเลี่ยงการล็อกตาย

รูปต่อไปนี้แสดงการติดตายที่พบได้ทั่วไป

การติดตายที่พบบ่อย

รูปที่ 7 การติดตายที่พบได้ทั่วไป

  1. กระบวนการ A ใช้ Mutex MA และเรียกใช้ Binder (T1) ไปยังกระบวนการ B ซึ่งพยายามใช้ Mutex MB ด้วย
  2. ในขณะเดียวกัน กระบวนการ B ใช้ Mutex MB และเรียกใช้ Binder (T2) ไปยังกระบวนการ A ซึ่งพยายามใช้ Mutex MA

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

หากต้องการหลีกเลี่ยงการล็อกตายขณะใช้ Binder โปรดอย่าล็อกไว้เมื่อเรียกใช้ Binder

กฎการจัดลำดับการล็อกและการล็อกตาย

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

Mutex เดียวและการล็อกตาย

ด้วยธุรกรรมแบบซ้อน กระบวนการ B สามารถเรียกใช้เธรดเดียวกันในกระบวนการ A ที่ใช้ Mutex ได้โดยตรง ดังนั้น การเรียกซ้ำที่ไม่คาดคิดอาจทำให้เกิดการติดตายด้วย Mutex เดียว

การเรียกใช้แบบอะซิงโครนัสและการล็อกตาย

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

Binder Thread เดียวและการล็อกตาย

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

กำหนดค่าขนาด Threadpool

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

ระบบจะสร้างเธรดตามความต้องการจนกว่าจะถึงค่าสูงสุดที่กำหนดค่าไว้ หลังจากสร้าง Binder Thread แล้ว เธรดจะทำงานอยู่จนกว่ากระบวนการที่โฮสต์เธรดจะสิ้นสุด

ไลบรารี libbinder มีเธรดเริ่มต้น 15 รายการ ใช้ setThreadPoolMaxThreadCount เพื่อเปลี่ยนค่านี้

using ::android::ProcessState;
ProcessState::self()->setThreadPoolMaxThreadCount(size_t maxThreads);