C++ Academy · บทเรียน

ตัวแปรเงื่อนไขและรูปแบบผู้ปฏิบัติงาน

ประสานงานเธรดด้วย std::condition_variable และรูปแบบกลุ่มผู้ปฏิบัติงานแบบคลาสสิก

บทเรียน 3 จาก 414 ขั้นตอน

ตัวแปรเงื่อนไขและรูปแบบผู้ปฏิบัติงาน เป็นบทเรียน C++ Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C++ Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C++ Academy มีบทเรียนทั้งหมด 4 บทเรียน

ตัวแปรเงื่อนไขคืออะไร

ตัวแปรเงื่อนไข ทำให้เธรดรอจนกว่าเธรดอีกตัวจะแจ้งว่าสภาวะหนึ่งเปลี่ยนแปลง ใช้สำหรับประสานงานระหว่างผู้ผลิตและผู้บริโภค

พื้นฐานของ std::condition_variable

ตัวแปรเงื่อนไขทำงานร่วมกับ std::mutex และเงื่อนไขกำกับ เธรดที่รอจะปล่อย lock ขณะรอ และเรียก lock กลับคืนเมื่อถูกปลุก

#include <condition_variable>
#include <mutex>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> tasks;
bool done = false;

การรอเงื่อนไข

ใช้ cv.wait(lock, predicate) ซึ่งจะปล่อย lock ของมิวเท็กซ์และให้เธรดหยุดพักจนกว่าจะได้รับการแจ้งเตือนแบบอะตอมิก เมื่อตื่นขึ้น เธรดจะเรียก lock กลับคืนและตรวจสอบเงื่อนไขกำกับ หากเป็นเท็จ ก็จะหยุดพักอีกครั้ง

std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !tasks.empty() || done; });

การแจ้งเตือน

เธรดผู้ผลิตจะเรียก cv.notify_one() หรือ cv.notify_all() หลังจากเปลี่ยนสถานะ โดยควรถือครองล็อกไว้ระหว่างการเปลี่ยนสถานะ

{
    std::lock_guard<std::mutex> lock(mtx);
    tasks.push(42);
}
cv.notify_one();

รูปแบบผู้ผลิต- consumer

ตัวอย่างคลาสสิก: ผู้ผลิตเพิ่มงานลงในคิว ส่วน consumer รอรับงาน

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !tasks.empty() || done; });
        if (done && tasks.empty()) break;
        int task = tasks.front();
        tasks.pop();
        lock.unlock();
        process(task);
    }
}

การตื่นขึ้นโดยไม่มีสัญญาณ

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

notify_one เทียบกับ notify_all

ใช้ notify_one เมื่อมีผู้รอเพียงหนึ่งรายที่สามารถทำงานต่อได้ ใช้ notify_all เมื่อการเปลี่ยนสถานะส่งผลต่อผู้รอทั้งหมด เช่น สัญญาณปิดระบบ

การรอแบบกำหนดเวลา

wait_for(lock, duration) และ wait_until(lock, time_point) จะตื่นขึ้นเมื่อได้รับการแจ้งเตือนหรือเมื่อหมดเวลา

if (cv.wait_for(lock, 1s, [] { return ready; })) {
    /* condition met */
} else {
    /* timed out */
}

โครงร่างกลุ่มเธรดทำงาน

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

การป้องกันการพลาดการตื่น

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

std::counting_semaphore (C++20)

สำหรับการส่งสัญญาณอย่างง่ายที่อาศัยการนับ C++20 ได้เพิ่ม std::counting_semaphore ซึ่งในบางรูปแบบใช้งานง่ายกว่าตัวแปรเงื่อนไข

ระวังภาวะติดตาย

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

ตรวจสอบความเข้าใจ

เหตุใด cv.wait(lock, predicate) จึงรับเพรดิเคต แม้ว่าคุณเพียงต้องการรอการแจ้งเตือน

สรุป

ตัวแปรเงื่อนไขใช้ประสานงานเธรดที่กำลังรอการเปลี่ยนสถานะ ใช้ wait(lock, predicate) เพื่อจัดการการตื่นขึ้นโดยไม่มีสัญญาณ แก้ไขสถานะภายใต้มิวเทกซ์ แล้วเรียก notify_one หรือ notify_all หลังจากนั้น

เริ่มต้นได้ฟรี

เรียนรู้ C++ ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
51
บทเรียน
203

คำถามที่พบบ่อย

บทเรียน “ตัวแปรเงื่อนไขและรูปแบบผู้ปฏิบัติงาน” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ตัวแปรเงื่อนไขและรูปแบบผู้ปฏิบัติงาน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C++ Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C++ Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ตัวแปรเงื่อนไขและรูปแบบผู้ปฏิบัติงาน”

ประสานงานเธรดด้วย std::condition_variable และรูปแบบกลุ่มผู้ปฏิบัติงานแบบคลาสสิก คุณปฏิบัติ C++ Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C++ Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C++ Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “ตัวแปรเงื่อนไขและรูปแบบผู้ปฏิบัติงาน” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C++ Academy นี้ได้ไหม

ได้ บทเรียน C++ Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. std::thread: การรวมและการแยกเธรด
  2. std::mutex, lock_guard และ unique_lock
  3. ตัวแปรเงื่อนไขและรูปแบบผู้ปฏิบัติงาน
  4. พื้นฐาน std::async และ std::future
← กลับไปที่ C++ Academy