CountDownLatch กับ Semaphore


94

มีประโยชน์จากการใช้

java.util.concurrent.CountdownLatch

แทน

java.util.concurrent.Semaphore ?

เท่าที่ฉันสามารถบอกได้ว่าชิ้นส่วนต่อไปนี้เกือบจะเทียบเท่า:

1. สัญญาณ

final Semaphore sem = new Semaphore(0);
for (int i = 0; i < num_threads; ++ i)
{
  Thread t = new Thread() {
    public void run()
    {
      try
      {
        doStuff();
      }
      finally
      {
        sem.release();
      }
    }
  };
  t.start();
}

sem.acquire(num_threads);

2: CountDownLatch

final CountDownLatch latch = new CountDownLatch(num_threads);
for (int i = 0; i < num_threads; ++ i)
{
  Thread t = new Thread() {
    public void run()
    {
      try
      {
        doStuff();
      }
      finally
      {
        latch.countDown();
      }
    }
  };
  t.start();
}

latch.await();

ยกเว้นว่าในกรณี # 2 สลักจะไม่สามารถใช้ซ้ำได้และที่สำคัญคุณต้องรู้ล่วงหน้าว่าจะสร้างเธรดจำนวนเท่าใด (หรือรอจนกว่าจะเริ่มทั้งหมดก่อนที่จะสร้างสลัก)

ดังนั้นในสถานการณ์ใดที่ควรใช้สลัก?

คำตอบ:


111

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

final CountDownLatch countdown = new CountDownLatch(1);

for (int i = 0; i < 10; ++ i) {
   Thread racecar = new Thread() {    
      public void run() {
         countdown.await(); //all threads waiting
         System.out.println("Vroom!");
      }
   };
   racecar.start();
}
System.out.println("Go");
countdown.countDown();   //all threads start now!

คุณยังสามารถใช้สิ่งนี้เป็น "อุปสรรค" สไตล์ MPI ที่ทำให้เธรดทั้งหมดรอให้เธรดอื่นมาถึงจุดหนึ่งก่อนที่จะดำเนินการต่อ

final CountDownLatch countdown = new CountDownLatch(num_thread);

for (int i = 0; i < num_thread; ++ i) {
   Thread t= new Thread() {    
      public void run() {
         doSomething();
         countdown.countDown();
         System.out.printf("Waiting on %d other threads.",countdown.getCount());
         countdown.await();     //waits until everyone reaches this point
         finish();
      }
   };
   t.start();
}

ทั้งหมดที่กล่าวมาCountDownLatchสามารถใช้ได้อย่างปลอดภัยในลักษณะที่คุณแสดงในตัวอย่างของคุณ


1
ขอบคุณ. ดังนั้นสองตัวอย่างของฉันจะไม่เท่ากันหากหลายเธรดสามารถรอบนสลักได้ ... เว้นแต่ sem.acquire (num_threads); ตามด้วย sem.release (num_threads);? ฉันคิดว่าจะทำให้พวกเขาเทียบเท่าอีกครั้ง
finnw

ในแง่หนึ่งใช่ตราบใดที่ทุกเธรดที่เรียกว่าได้รับตามด้วยการเปิดตัว พูดอย่างเคร่งครัดไม่ เมื่อใช้สลักเธรดทั้งหมดมีสิทธิ์เริ่มต้นพร้อมกัน ด้วยเซมาฟอร์พวกมันจะมีสิทธิ์ทีละรายการ (ซึ่งอาจส่งผลให้การตั้งเวลาเธรดต่างกัน)
James Schek

เอกสาร Java ดูเหมือนจะบ่งบอกนี้พอดี CountdownLatch ดีกับตัวอย่างของเขา: docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/... โดยเฉพาะอย่างยิ่ง "สามารถใช้ CountDownLatch ที่เริ่มต้นเป็น N เพื่อทำให้เธรดหนึ่งรายการรอจนกว่าเธรด N จะดำเนินการบางอย่างเสร็จสิ้นหรือการดำเนินการบางอย่างเสร็จสิ้นแล้ว N ครั้ง
Chris Morris

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

11
สิ่งนี้ตอบคำถามCountDownLatch ที่ใช้บ่อยที่สุดคืออะไร? ไม่ตอบคำถามเดิมเกี่ยวกับข้อดี / ความแตกต่างของการใช้ CountDownLatch บน Semaphore
Marco Lackovic

67

CountDownLatchใช้เพื่อเริ่มชุดของเธรดจากนั้นรอจนกว่าทั้งหมดจะเสร็จสมบูรณ์ (หรือจนกว่าจะเรียกcountDown()ตามจำนวนครั้งที่กำหนด

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

ในตัวอย่างของคุณเป็นหลักโดยใช้สัญญาณเป็นจัดเรียงของการนับUPกลอน ระบุว่าเจตนาของคุณคือรอให้ทุกเธรดจบโดยใช้CountdownLatchทำให้ความตั้งใจของคุณชัดเจนขึ้น


23

สรุปสั้น ๆ:

  1. SemaphoreและตอบCountDownLatchสนองวัตถุประสงค์ที่แตกต่างกัน

  2. ใช้Semaphoreเพื่อควบคุมเธรดการเข้าถึงทรัพยากร

  3. ใช้CountDownLatchเพื่อรอให้เธรดทั้งหมดเสร็จสมบูรณ์

Semaphore คำจำกัดความจาก Javadocs:

A Semaphoreรักษาชุดของใบอนุญาต แต่ละacquire()บล็อกถ้าจำเป็นจนกว่าจะมีใบอนุญาตแล้วจึงรับ แต่ละคนrelease()จะเพิ่มใบอนุญาตซึ่งอาจปล่อยผู้ได้รับการบล็อก

อย่างไรก็ตามไม่มีการใช้วัตถุอนุญาตจริง Semaphoreเพียงแค่ช่วยให้การนับจำนวนที่มีอยู่และทำหน้าที่ตาม

มันทำงานอย่างไร?

Semaphores ใช้เพื่อควบคุมจำนวนเธรดพร้อมกันที่กำลังใช้รีซอร์สทรัพยากรนั้นอาจเป็นข้อมูลที่แชร์หรือบล็อกโค้ด ( ส่วนที่สำคัญ ) หรือไฟล์ใด ๆ ก็ได้

นับบนSemaphoreสามารถไปขึ้นและลงเป็นหัวข้อที่แตกต่างกันโทรและacquire() release()แต่เมื่อใดก็ตามคุณจะมีจำนวนเธรดมากกว่าจำนวนเซมาฟอร์ไม่ได้

Semaphore ใช้กรณี:

  1. การ จำกัด การเข้าถึงดิสก์พร้อมกัน (สิ่งนี้สามารถทำลายประสิทธิภาพเนื่องจากการค้นหาดิสก์ที่แข่งขันกัน)
  2. การ จำกัด การสร้างเธรด
  3. การเชื่อมต่อ JDBC การรวม / การ จำกัด
  4. การควบคุมการเชื่อมต่อเครือข่าย
  5. การควบคุม CPU หรืองานที่ต้องใช้หน่วยความจำมาก

ดูบทความนี้สำหรับการใช้เซมาฟอร์

CountDownLatch คำจำกัดความจาก Javadocs:

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

มันทำงานอย่างไร?

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

CountDownLatch ใช้กรณี:

  1. การบรรลุความเท่าเทียมกันสูงสุด: บางครั้งเราต้องการเริ่มเธรดจำนวนมากพร้อมกันเพื่อให้ได้ความเท่าเทียมกันสูงสุด
  2. รอ N เธรดให้เสร็จสิ้นก่อนเริ่มดำเนินการ
  3. การตรวจจับการหยุดชะงัก

ดูบทความนี้เพื่อทำความเข้าใจCountDownLatchแนวคิดอย่างชัดเจน

ลองดูFork Join Poolที่บทความนี้ด้วย มันมีความคล้ายคลึงกับCountDownLatch.


7

สมมติว่าคุณเดินเข้าไปในร้านโปรกอล์ฟหวังว่าจะได้พบกับสี่คน

เมื่อคุณยืนต่อแถวเพื่อรับเวลาออกรอบจากพนักงานร้านมืออาชีพคนหนึ่งโดยพื้นฐานแล้วคุณโทรหาproshopVendorSemaphore.acquire()เมื่อคุณได้เวลาออกรอบแล้วคุณจะโทรไปproshopVendorSemaphore.release()หมายเหตุ: ผู้เข้าร่วมฟรีคนใดคนหนึ่งสามารถให้บริการคุณได้เช่นทรัพยากรที่ใช้ร่วมกัน

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

ตอนนี้หลังจากเก้าหลุมเมื่อคุณแต่ละคนหยุดพักสมมุติให้มีส่วนร่วมในการเริ่มต้นอีกครั้งเขาใช้ 'ใหม่' CountDownLatch(4)เพื่อทีออฟหลุม 10 รอ / ซิงค์เดียวกันกับหลุม 1

อย่างไรก็ตามหากสตาร์ทเตอร์ใช้ a CyclicBarrierเพื่อเริ่มต้นเขาสามารถรีเซ็ตอินสแตนซ์เดียวกันในหลุม 10 แทนสลักที่สองซึ่งใช้ & โยน


1
ฉันไม่แน่ใจว่าฉันเข้าใจคำตอบของคุณ แต่ถ้าคุณกำลังพยายามอธิบายว่า CountdownLatch และ Semaphore ทำงานอย่างไรนั่นไม่ใช่ประเด็นของคำถาม
finnw

10
น่าเสียดายที่ฉันไม่รู้อะไรเกี่ยวกับกอล์ฟ
ผู้ถือแหวน

แต่สิ่งที่เริ่มต้นสามารถทำได้เช่นกันกับ. accire (ผู้เล่น) และเพิ่มจำนวนรีเลสด้วยการเปิดตัว การนับถอยหลังดูเหมือนจะมีฟังก์ชันการทำงานน้อยลงและไม่มีการนำกลับมาใช้ใหม่
Lassi Kinnunen

1

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


0

CountdownLatchทำให้เธรดรอบนawait()เมธอดจนกว่าเวลาดังกล่าวจะนับถึงศูนย์ บางทีคุณอาจต้องการให้เธรดทั้งหมดของคุณรอจนกว่าจะมีการเรียกใช้บางสิ่ง 3 ครั้งเธรดทั้งหมดก็ไปได้ Latchโดยทั่วไปจะไม่สามารถตั้งค่า

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

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