จะตรวจสอบได้อย่างไรว่าเธรด std :: ยังทำงานอยู่หรือไม่


86

ฉันจะตรวจสอบได้อย่างไรว่า a std::threadยังทำงานอยู่ (ในรูปแบบที่ไม่ขึ้นกับแพลตฟอร์ม) มันขาดtimed_join()วิธีการและjoinable()ไม่มีความหมายสำหรับสิ่งนั้น

ฉันคิดว่าจะล็อค mutex ด้วยstd::lock_guardเธรดและใช้try_lock()วิธีการของ mutex เพื่อตรวจสอบว่ามันยังคงถูกล็อกอยู่หรือไม่ (เธรดกำลังทำงานอยู่) แต่ดูเหมือนว่าฉันจะซับซ้อนโดยไม่จำเป็น

คุณรู้วิธีที่หรูหรากว่านี้หรือไม่?

อัปเดต:เพื่อความชัดเจน: ฉันต้องการตรวจสอบว่าเธรดออกอย่างสมบูรณ์หรือไม่ ถือว่าด้าย 'ห้อย' ทำงานเพื่อจุดประสงค์นี้


ฉันเดาว่าการตรวจสอบว่าเธรดยังคงทำงานอยู่มีความสำคัญเฉพาะเมื่อคุณคาดหวังwait()ไว้หรือไม่และถ้าเป็นเช่นนั้นหากคุณยังไม่ได้แก้ไขแสดงว่าเธรดwait()จะต้องทำงานตามคำจำกัดความ แต่การให้เหตุผลนี้อาจไม่ตรงประเด็น
เมื่อ

อันที่จริงฉันมีเธรดที่ออกจากเงื่อนไขพิเศษและฉันต้องการตรวจสอบจากเธรดหลักว่ายังทำงานอยู่หรือไม่ แต่ไม่ต้องการรอ (เข้าร่วม)
kispaljr

1
วิ่งหมายถึงอะไรกันแน่? คุณหมายถึงกำลังประมวลผลมากกว่าอยู่ในสถานะรอหรือคุณหมายถึงเธรดยังคงมีอยู่และยังไม่สิ้นสุด?
CashCow

คุณสามารถใช้
บูสต์

4
คุณไม่ควรยอมรับคำตอบหากคุณไม่พอใจกับมัน
Nicol Bolas

คำตอบ:


120

หากคุณยินดีที่จะใช้ C ++ 11 std::asyncและstd::futureเพื่อรันงานของคุณคุณสามารถใช้wait_forฟังก์ชันstd::futureเพื่อตรวจสอบว่าเธรดยังคงทำงานอย่างเป็นระเบียบเช่นนี้หรือไม่:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    /* Run some task on new thread. The launch policy std::launch::async
       makes sure that the task is run asynchronously on a new thread. */
    auto future = std::async(std::launch::async, [] {
        std::this_thread::sleep_for(3s);
        return 8;
    });

    // Use wait_for() with zero milliseconds to check thread status.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    auto result = future.get(); // Get result.
}

หากคุณต้องใช้std::threadคุณสามารถใช้std::promiseเพื่อรับวัตถุในอนาคต:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a promise and get its future.
    std::promise<bool> p;
    auto future = p.get_future();

    // Run some task on a new thread.
    std::thread t([&p] {
        std::this_thread::sleep_for(3s);
        p.set_value(true); // Is done atomically.
    });

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

ตัวอย่างทั้งสองนี้จะแสดงผลลัพธ์:

Thread still running

แน่นอนว่านี่เป็นเพราะสถานะเธรดถูกตรวจสอบก่อนงานจะเสร็จสิ้น

แต่อีกครั้งมันอาจจะง่ายกว่าที่จะทำเหมือนที่คนอื่นพูดถึงไปแล้ว:

#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    std::atomic<bool> done(false); // Use an atomic flag.

    /* Run some task on a new thread.
       Make sure to set the done flag to true when finished. */
    std::thread t([&done] {
        std::this_thread::sleep_for(3s);
        done = true;
    });

    // Print status.
    if (done) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

แก้ไข:

นอกจากนี้ยังมีstd::packaged_taskสำหรับใช้กับstd::threadโซลูชันที่สะอาดกว่าการใช้std::promise:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a packaged_task using some task and get its future.
    std::packaged_task<void()> task([] {
        std::this_thread::sleep_for(3s);
    });
    auto future = task.get_future();

    // Run task on new thread.
    std::thread t(std::move(task));

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        // ...
    }

    t.join(); // Join thread.
}

2
คำตอบที่ดี ฉันจะเพิ่มว่ามันยังใช้งานได้กับเธรดโดยไม่มีค่าตอบแทนและอนาคต <void>
kispaljr

รหัสนี้มีเหตุผลstd::atomic<bool> done(false);อะไร? ไม่ได้เป็นboolอะตอมโดยค่าเริ่มต้น?
Hi-Angel

6
@YagamyLight ใน C ++ ไม่มีอะไรเป็นปรมาณูตามค่าเริ่มต้นเว้นแต่จะถูกรวมไว้ในstd::atomicไฟล์. sizeof(bool)มีการกำหนดการใช้งานและอาจเป็น> 1 ดังนั้นจึงเป็นไปได้ที่จะเกิดการเขียนบางส่วน นอกจากนี้ยังมีปัญหาการเชื่อมโยงกันของแคช ..
Snps

2
@YagamyLight ข้อมูลเพิ่มเติมที่นี่: เมื่อใดที่ฉันต้องใช้อะตอม <bool> แทนบูลจริงๆ
Snps

1
โปรดทราบว่า std :: chrono_literals จะต้องใช้ C ++ 14 ในการคอมไพล์
Patrizio Bertoni

5

วิธีแก้ปัญหาง่ายๆคือการมีตัวแปรบูลีนที่เธรดตั้งค่าเป็นจริงในช่วงเวลาปกติและจะถูกตรวจสอบและตั้งค่าเป็นเท็จโดยเธรดที่ต้องการทราบสถานะ หากตัวแปรเป็นเท็จสำหรับ long แสดงว่าเธรดจะไม่ทำงานอีกต่อไป

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

อย่างไรก็ตามโปรดทราบว่าไม่มีวิธีใดใน C ++ 11 ที่จะฆ่าหรือลบเธรดที่แขวนคอได้

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

แก้ไข 2หากเธรดออกเนื่องจากข้อยกเว้นจะมีฟังก์ชัน "main" สองเธรด: อันแรกมีtry- catchภายในซึ่งเรียกฟังก์ชันเธรดหลักที่สอง "จริง" ฟังก์ชันหลักแรกนี้ตั้งค่าตัวแปร "have_exited" สิ่งนี้:

bool thread_done = false;

void *thread_function(void *arg)
{
    void *res = nullptr;

    try
    {
        res = real_thread_function(arg);
    }
    catch (...)
    {
    }

    thread_done = true;

    return res;
}

1
ถ้านั่นคือคำจำกัดความของ OP ของคำว่า "วิ่ง"
CashCow

บางทีคุณอาจเข้าใจฉันผิด ฉันต้องการตรวจสอบว่าเธรดได้ออกอย่างหมดจดหรือไม่ ขออภัยในการใช้ถ้อยคำที่ไม่ชัดเจน
kispaljr

7
หากเธรดอื่นกำลังอ่านและเขียนthread_doneโค้ดนี้จะเสียโดยไม่มีสิ่งกีดขวางหน่วยความจำ ใช้std::atomic<bool>แทน
ildjarn

1
ฉันไม่ได้อ้างถึงเธรดของผู้ปฏิบัติงานหลายเธรดฉันกำลังอ้างถึงเธรดของผู้ปฏิบัติงานคนเดียวที่เขียนในboolขณะที่เธรดหลักอ่านจากเธรด - สิ่งนี้ต้องการอุปสรรคด้านความจำ
ildjarn

3
ตรวจสอบคำถามนี้สำหรับการสนทนาว่าเหตุใดจึงstd::atomic<bool>จำเป็นที่นี่
Robert Rüger

2

กลไกง่ายๆนี้คุณสามารถใช้เพื่อตรวจจับการจบเธรดโดยไม่ต้องปิดกั้นในวิธีการรวม

std::thread thread([&thread]() {
    sleep(3);
    thread.detach();
});

while(thread.joinable())
    sleep(1);

2
ในที่สุดการถอดเธรดไม่ใช่สิ่งที่เราต้องการและถ้าคุณไม่ทำคุณต้องเรียกใช้join()จากเธรดบางส่วนที่ไม่รอให้เธรดหลุดjoinable()คุณสมบัติมิฉะนั้นจะวนซ้ำไม่รู้จบ (เช่นjoinable()ส่งคืนจริงจนกว่าเธรดจะเป็นจริงjoin()เอ็ดและจนกว่าจะเสร็จ)
Niklas R

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

ฉันทำและประเด็นที่ฉันพยายามทำคือถ้าคุณลบthread.detach()ส่วนนั้นออกโปรแกรมข้างต้นจะไม่มีวันยุติ
Niklas R

ใช่มันจะไม่ จึงเป็นสาเหตุที่เรียกว่าการปลดในที่สุด
Evgeny Karpov

1
วิธีการเรียกการถอดแยกออกจากความต้องการ mutex และโซลูชันอื่น ๆ ที่ซับซ้อนมากขึ้น ฉันใช้มันและได้ผล! ขอบคุณสำหรับคำตอบ.
ก.ย.

1

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

ปัญหาเล็ก ๆ อย่างหนึ่งของปัญหานี้ mutex.try_lock () จะส่งกลับเท็จระหว่างเวลาที่สร้างเธรดและเมื่อล็อก mutex แต่สามารถหลีกเลี่ยงได้โดยใช้วิธีที่ซับซ้อนกว่าเล็กน้อย


-1 คุณไม่ควรใช้std::mutexสำหรับการส่งสัญญาณประเภทนี้ (ส่วนใหญ่เกิดจากเหตุผลในการใช้งาน mutex) ใช้atomic_flagงานได้ดีในกรณีนี้โดยมีค่าใช้จ่ายน้อยลง A std::futureอาจจะดีกว่าเพราะแสดงเจตนาชัดเจนกว่า นอกจากนี้โปรดจำไว้ว่าtry_lockอาจล้มเหลวอย่างปลอมแปลงดังนั้นการส่งคืนจึงไม่จำเป็นต้องเป็นสถานะของเธรด (แม้ว่าในกรณีนี้อาจไม่ได้รับบาดเจ็บมากนัก)
ComicSansMS

1

คุณสามารถตรวจสอบได้ตลอดเวลาว่า id ของเธรดแตกต่างจาก std :: thread :: id () default ที่สร้างขึ้นหรือไม่ เธรดที่กำลังรันมี id ที่เกี่ยวข้องของแท้เสมอ พยายามหลีกเลี่ยงของแฟนซีมากเกินไป :)


0

แน่นอนว่าต้องเริ่มต้นตัวแปรที่ห่อหุ้ม mutex ไว้falseซึ่งเธรดจะตั้งค่าtrueเป็นสิ่งสุดท้ายที่ทำก่อนที่จะออก ปรมาณูนั้นเพียงพอสำหรับความต้องการของคุณหรือไม่?


1
ถ้าคุณใช้ mutex อยู่แล้วฉันรู้สึกว่าวิธีแก้ปัญหาของฉัน (ใช้เฉพาะ mutex โดยไม่มีบูลีน) จะดูสง่างามมากขึ้น หากคุณต้องการใช้บูลีนที่ปลอดภัยกับเธรดฉันขอแนะนำให้ใช้ std :: atomic <bool> แทน ในการใช้งานส่วนใหญ่จะไม่มีการล็อค
kispaljr

ล็อคเลยทำไม? หนึ่งเธรดเท่านั้นที่เคยอ่านหนึ่งเธรดที่เคยเขียน และการเขียนขนาดคำเป็นปรมาณูในทุกกรณี IIRC
Xeo

1
@Xeo: การเขียนอาจเป็นแบบปรมาณู แต่ยังคงจำเป็นต้องมีการกั้นหน่วยความจำหากคุณคาดว่าจะเห็นค่าที่เขียนบนเธรดอื่น (ซึ่งอาจทำงานบน CPU อื่น) std::atomic<bool>ดูแลสิ่งนี้ให้คุณด้วยเหตุนี้ IMO จึงเป็นคำตอบที่แท้จริง
ildjarn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.