Làm thế nào để kiểm tra xem một chuỗi std :: vẫn đang chạy?


84

Làm cách nào để kiểm tra xem a std::threadvẫn đang chạy (theo một cách độc lập với nền tảng)? Nó thiếu một timed_join()phương pháp và joinable()không có nghĩa là cho điều đó.

Tôi đã nghĩ đến việc khóa mutex bằng một std::lock_guardtrong chuỗi và sử dụng try_lock()phương pháp của mutex để xác định xem nó có còn bị khóa hay không (luồng đang chạy), nhưng nó có vẻ phức tạp không cần thiết đối với tôi.

Bạn có biết một phương pháp thanh lịch hơn?

Cập nhật: Để rõ ràng: Tôi muốn kiểm tra xem chuỗi đã thoát sạch hay chưa. Một chuỗi 'treo' được coi là đang chạy cho mục đích này.


Tôi đoán việc kiểm tra xem một chuỗi có còn chạy hay không chỉ quan trọng khi bạn mong đợi wait()nó và nếu có, nếu bạn chưa chỉnh sửa wait()nó, nó phải đang chạy theo định nghĩa. Nhưng lý do này có thể không chính xác.
vào

Thật sự tôi có một sợi mà thoát vào điều kiện đặc biệt, và tôi muốn kiểm tra từ các chủ đề chính nếu nó vẫn đang chạy, nhưng không muốn phải chờ (tham gia) nó
kispaljr

1
Chính xác thì ý bạn là gì khi chạy? Ý của bạn là nó đang tích cực xử lý thay vì ở trạng thái chờ, hay ý bạn là luồng vẫn tồn tại và chưa kết thúc?
CashCow

Bạn luôn có thể sử dụng boost :)
CashCow

4
Bạn không nên chấp nhận một câu trả lời nếu bạn không hài lòng với nó.
Nicol Bolas

Câu trả lời:


117

Nếu bạn sẵn sàng sử dụng C ++ 11 std::asyncstd::futuređể chạy các tác vụ của mình, thì bạn có thể sử dụng wait_forchức năng std::futuređể kiểm tra xem luồng vẫn đang chạy theo cách gọn gàng như sau:

#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.
}

Nếu bạn phải sử dụng std::threadthì bạn có thể sử dụng std::promiseđể lấy một đối tượng trong tương lai:

#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.
}

Cả hai ví dụ này sẽ xuất ra:

Thread still running

Điều này là tất nhiên vì trạng thái luồng được kiểm tra trước khi tác vụ kết thúc.

Nhưng một lần nữa, có thể đơn giản hơn nếu chỉ làm như những người khác đã đề cập:

#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.
}

Biên tập:

Ngoài ra còn có cách std::packaged_tasksử dụng std::threadcho một giải pháp sạch hơn là sử dụng 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
Câu trả lời hay. Tôi muốn nói thêm rằng nó cũng làm việc với các chủ đề mà không cần bất kỳ giá trị trả về và tương lai <trống>
kispaljr

Lý do cho mã này là std::atomic<bool> done(false);gì? Không phải là boolnguyên tử theo mặc định?
Hi-Angel

6
@YagamyLight Trong C ++ không có gì là nguyên tử theo mặc định trừ khi nó được bao bọc trong một std::atomic. sizeof(bool)là triển khai được xác định và có thể> 1, vì vậy có thể xảy ra ghi một phần. Ngoài ra còn có vấn đề về tính liên kết của bộ nhớ cache ..
Snps


1
lưu ý rằng std :: chrono_literals sẽ đòi hỏi C ++ 14 để biên dịch
Patrizio Bertoni

6

Một giải pháp dễ dàng là có một biến boolean mà luồng đặt thành true trong các khoảng thời gian đều đặn, và biến đó được kiểm tra và đặt thành false bởi luồng muốn biết trạng thái. Nếu biến là false trong thời gian dài thì luồng không còn được coi là hoạt động.

Một cách an toàn hơn cho luồng là có một bộ đếm được tăng lên bởi luồng con và luồng chính so sánh bộ đếm với một giá trị được lưu trữ và nếu giống nhau sau một thời gian quá dài thì luồng con được coi là không hoạt động.

Tuy nhiên, lưu ý rằng không có cách nào trong C ++ 11 để thực sự giết hoặc loại bỏ một luồng đã bị treo.

Chỉnh sửa Cách kiểm tra xem một luồng đã thoát sạch hay chưa: Về cơ bản, kỹ thuật tương tự như được mô tả trong đoạn đầu tiên; Có một biến boolean được khởi tạo thành false. Điều cuối cùng mà chuỗi con làm là đặt nó thành true. Sau đó, luồng chính có thể kiểm tra biến đó và nếu đúng, hãy tham gia vào luồng con mà không bị chặn nhiều (nếu có).

Edit2 Nếu luồng thoát do một ngoại lệ, thì có hai chức năng "chính" của luồng: Chức năng đầu tiên có a try- catchbên trong mà nó gọi là chức năng luồng chính "thực" thứ hai. Hàm chính đầu tiên này đặt biến "has_exited". Một cái gì đó như thế này:

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
Nếu đó là định nghĩa của OP về "chạy".
CashCow

Có lẽ bạn đã hiểu lầm tôi. Tôi muốn kiểm tra xem một chủ đề đã thoát sạch hay chưa. Xin lỗi vì từ ngữ không rõ ràng.
kispaljr

7
Nếu các luồng khác nhau đang đọc và ghi thread_done, thì mã này bị hỏng mà không có rào cản bộ nhớ. Sử dụng std::atomic<bool>thay thế.
ildjarn

1
Tôi không đề cập đến nhiều luồng công nhân, tôi đang đề cập đến một luồng công nhân duy nhất ghi vào booltrong khi luồng chính đọc từ nó - điều này cần một rào cản bộ nhớ.
ildjarn

3
Kiểm tra câu hỏi này để thảo luận tại sao a std::atomic<bool>là cần thiết ở đây.
Robert Rüger

3

Cơ chế đơn giản này bạn có thể sử dụng để phát hiện kết thúc một luồng mà không chặn trong phương thức nối.

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

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

2
tách luồng cuối cùng không phải là điều người ta muốn, và nếu bạn không muốn, bạn phải gọi join()từ một số luồng không đợi luồng đó nới lỏng thuộc tính của nó joinable(), nếu không nó sẽ lặp vô tận (tức là joinable()trả về true cho đến khi luồng thực sự là join()ed và không cho đến khi kết thúc của nó)
Niklas R

joinable có nghĩa là một luồng đang giữ một tay cầm luồng. nếu chủ đề được thực hiện, nó sẽ vẫn có thể nối được. nếu bạn cần kiểm tra mà không cần đợi kết thúc luồng, đây là giải pháp. Nó chỉ là một vài dòng mã. Tại sao bạn không thử nó trước?
Evgeny Karpov,

Tôi đã làm, và điểm tôi đang cố gắng thực hiện là nếu bạn loại bỏ thread.detach()phần này, chương trình ở trên sẽ không bao giờ kết thúc.
Niklas R,

có, nó sẽ không. đó là lý do tại sao nó gọi là tách ra cuối cùng.
Evgeny Karpov,

1
Phương pháp gọi phân phối tách rời này có nhu cầu về mutex và các giải pháp phức tạp hơn khác. Tôi sử dụng nó và nó hoạt động! Cảm ơn vì câu trả lời.
Tháng Chín

1

Tạo mutex mà cả chuỗi đang chạy và chuỗi đang gọi đều có quyền truy cập. Khi chuỗi đang chạy bắt đầu, nó sẽ khóa mutex và khi nó kết thúc, nó sẽ mở khóa mutex. Để kiểm tra xem tiểu trình có còn chạy hay không, tiểu trình đang gọi gọi mutex.try_lock (). Giá trị trả về của đó là trạng thái của luồng. (Chỉ cần đảm bảo mở khóa mutex nếu try_lock hoạt động)

Một vấn đề nhỏ với điều này, mutex.try_lock () sẽ trả về false giữa thời điểm luồng được tạo và khi nó khóa mutex, nhưng điều này có thể tránh được bằng cách sử dụng một phương pháp phức tạp hơn một chút.


-1 Bạn không nên sử dụng std::mutexcho loại tín hiệu này (chủ yếu là do lý do cách mutex thường được thực hiện). An atomic_flaghoạt động tốt trong trường hợp này với chi phí ít hơn. A std::futurethậm chí có thể tốt hơn vì nó thể hiện ý định rõ ràng hơn. Ngoài ra, hãy nhớ rằng try_lockcó thể thất bại một cách nhanh chóng, vì vậy kết quả trả về không nhất thiết phải là trạng thái của luồng (mặc dù nó có thể sẽ không ảnh hưởng nhiều đến bạn trong trường hợp cụ thể này).
ComicSansMS

1

Bạn luôn có thể kiểm tra xem id của luồng có khác với std :: thread :: id () được xây dựng mặc định hay không. Một chuỗi đang chạy luôn có một id liên kết chính hãng. Cố gắng tránh quá nhiều thứ lạ mắt :)


0

Chắc chắn có một biến được bọc mutex được khởi tạo false, mà luồng đặt thành trueđiều cuối cùng nó thực hiện trước khi thoát. Nguyên tử đó có đủ cho nhu cầu của bạn không?


1
Nếu bạn vẫn sử dụng mutex, thì tôi cảm thấy giải pháp của tôi (chỉ sử dụng mutex, w / o là boolean) thanh lịch hơn. Nếu bạn thực sự muốn sử dụng boolean an toàn cho luồng, tôi khuyên bạn nên sử dụng std :: atom <bool> để thay thế. Trong hầu hết các triển khai, nó sẽ không bị khóa.
kispaljr

Tại sao lại khóa? Một luồng chỉ bao giờ đọc, một chỉ viết. Và kích thước chữ viết là nguyên tử trong mọi trường hợp IIRC.
Xeo

1
@Xeo: Việc ghi có thể là nguyên tử, nhưng vẫn cần một rào cản bộ nhớ nếu bạn muốn thấy giá trị được ghi trên một luồng khác (có thể đang thực thi trên một CPU khác). std::atomic<bool>giải quyết vấn đề này cho bạn, đó là lý do tại sao đó là câu trả lời thực sự IMO.
ildjarn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.