【问题标题】:How to check if a std::thread is still running?如何检查 std::thread 是否仍在运行?
【发布时间】:2012-02-24 01:12:40
【问题描述】:

如何检查std::thread 是否仍在运行(以独立于平台的方式)? 它缺少timed_join() 方法,而joinable() 并不适用于此。

我曾想过在线程中用std::lock_guard 锁定一个互斥锁,并使用互斥锁的try_lock() 方法来确定它是否仍处于锁定状态(线程正在运行),但这对我来说似乎不必要地复杂。

你知道更优雅的方法吗?

更新:要明确:我想检查线程是否干净退出。出于此目的,“挂起”线程被视为正在运行。

【问题讨论】:

  • 我想检查一个线程是否仍在运行仅在您期望 wait() 时才重要,如果是这样,如果您还没有 wait() 'ed 对于它,他一定是按定义运行。但这种推理可能不准确。
  • 其实我有一个线程在异常情况下退出,我想从主线程检查它是否还在运行,但不想等待(加入)它
  • 跑步到底是什么意思?你的意思是它正在积极处理而不是处于等待状态,还是你的意思是线程仍然存在并且没有终止?
  • 你总是可以使用 boost :)
  • 如果您对答案不满意,您不应该接受它。

标签: c++ multithreading c++11 stdthread


【解决方案1】:

一个简单的解决方案是有一个布尔变量,线程定期将其设置为 true,然后由想要知道状态的线程检查并设置为 false。如果变量为 false 太长,则线程不再被视为活动。

一个更线程安全的方法是有一个由子线程增加的计数器,并且主线程将计数器与存储的值进行比较,如果在太长时间后相同,则认为子线程不活动。

但是请注意,在 C++11 中没有办法真正杀死或删除已挂起的线程。

编辑如何检查线程是否干净退出:与第一段中描述的技术基本相同;将布尔变量初始化为 false。子线程做的最后一件事是将其设置为 true。然后主线程可以检查该变量,如果为 true,则在子线程上进行连接,而不会出现太多(如果有的话)阻塞。

Edit2 如果线程因异常而退出,则有两个线程“主”函数:第一个有一个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;
}

【讨论】:

  • 如果那是 OP 对“运行”的定义。
  • 也许你误会了我。我想检查一个线程是否干净地退出。很抱歉措辞不清楚。
  • 如果不同的线程正在读写thread_done,那么这段代码在没有内存屏障的情况下被破坏。请改用std::atomic<bool>
  • 我指的不是多个工作线程,我指的是单个工作线程写入bool,而主线程从中读取——这需要记忆障碍。
  • 查看this question 讨论为什么这里需要std::atomic<bool>
【解决方案2】:

肯定有一个互斥包装变量初始化为false,线程设置为true,作为退出前的最后一件事。原子性足以满足您的需求吗?

【讨论】:

  • 如果您仍然使用互斥锁,那么我觉得我的解决方案(仅使用互斥锁,不使用布尔值)更优雅。如果您绝对想使用线程安全的布尔值,我会推荐 std::atomic 。在大多数实现中,它将是无锁的。
  • 为什么要锁定?一个线程只读取,一个线程只写入。在任何情况下,字长写入都是原子的 IIRC。
  • @Xeo :写入可能是原子的,但是如果您希望在不同的线程上看到写入的值(可能在不同的 CPU 上执行),仍然需要内存屏障。 std::atomic<bool> 会为您解决这个问题,这就是为什么 这是 IMO 的真正答案。
【解决方案3】:

创建一个运行线程和调用线程都可以访问的互斥锁。当正在运行的线程启动时,它锁定互斥锁,当它结束时,它解锁互斥锁。要检查线程是否仍在运行,调用线程调用 mutex.try_lock()。它的返回值是线程的状态。 (如果 try_lock 有效,请确保解锁互斥锁)

与此有关的一个小问题是,mutex.try_lock() 将在创建线程和锁定互斥锁之间返回 false,但使用稍微复杂的方法可以避免这种情况。

【讨论】:

  • -1 你不应该使用std::mutex 来发送这种信号(主要是由于互斥锁通常是如何实现的)。在这种情况下,atomic_flag 也可以工作,而且开销更少。 std::future 可能会更好,因为它更清楚地表达了意图。另外,请记住 try_lock 可能会虚假失败,因此返回不一定一定是线程的状态(尽管在这种特殊情况下它可能不会对您造成太大伤害)。
【解决方案4】:

如果你愿意使用 C++11 std::asyncstd::future 来运行你的任务,那么你可以利用 std::futurewait_for 函数来检查线程是否仍在运行像这样的巧妙方式:

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

【讨论】:

  • 不错的答案。我要补充一点,它也适用于没有任何返回值和 future 的线程
  • 这段代码std::atomic&lt;bool&gt; done(false);的原因是什么? bool 默认不是原子的吗?
  • @YagamyLight 在 C++ 中,默认情况下没有任何东西是原子的,除非它被包裹在 std::atomic 中。 sizeof(bool) 是实现定义的,可能 > 1,因此可能会发生部分写入。还有缓存一致性的问题..
  • 请注意,std::chrono_literals 需要 C++14 才能编译
【解决方案5】:

这种简单的机制可以用来检测线程的结束,而不会在连接方法中阻塞。

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

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

【讨论】:

  • 分离线程最终不是人们想要的,如果你不这样做,你必须从不等待线程释放其joinable()属性的某个线程调用join(),否则它将无限循环(即joinable() 返回true,直到线程实际上是join()ed 而不是直到它完成)
  • joinable 表示线程持有线程句柄。如果线程完成,它仍然可以连接。如果您需要在不等待线程结束的情况下进行检查,这是解决方案。它只是几行代码。为什么不先尝试?
  • 我做到了,我想说的是,如果你删除 thread.detach() 部分,上面的程序将永远不会终止。
  • 是的,它不会。这就是为什么它最终调用分离。
  • 这种调用分离的方法不需要互斥锁和其他更复杂的解决方案。我使用它并且它有效!感谢您的回答。
【解决方案6】:

您可以随时检查线程的 id 是否不同于 std::thread::id() 默认构造的。 一个正在运行的线程总是有一个真正的关联 id。 尽量避免花哨的东西:)

【讨论】:

    【解决方案7】:

    我检查了两个系统: - 使用线程+原子:耗时 9738 毫秒 - 使用future+async:耗时7746毫秒 非线程:56000 毫秒 使用 Core-I7 6 核笔记本电脑

    我的代码创建了 4000 个线程,但每次运行不超过 12 个。

    代码如下:

    #include <iostream>
    #include <thread>
    #include <future>
    #include <chrono>
    #include <mutex>          // std::mutex
    #include <atomic>
    #include <chrono>
    
    
    #pragma warning(disable:4996)
    #pragma warning(disable:6031)
    #pragma warning(disable:6387)//strout
    #pragma warning(disable:26451)
    
    using namespace std;
    const bool FLAG_IMPRIME = false;
    const int MAX_THREADS = 12;
    
    
    mutex mtx;           // mutex for critical section
    atomic <bool> th_end[MAX_THREADS];
    atomic <int> tareas_acabadas;
    
    typedef std::chrono::high_resolution_clock t_clock; //SOLO EN WINDOWS
    std::chrono::time_point<t_clock> start_time, stop_time; char null_char;
    void timer(const char* title = 0, int data_size = 1) { stop_time = t_clock::now(); double us = (double)chrono::duration_cast<chrono::microseconds>(stop_time - start_time).count(); if (title) printf("%s time = %7lgms = %7lg MOPs\n", title, (double)us * 1e-3, (double)data_size / us); start_time = t_clock::now(); }
    
    
    
    class c_trim
    {
        char line[200];
        thread th[MAX_THREADS];
        double th_result[MAX_THREADS];
        int th_index;
        double milliseconds_commanded;
        void hilo(int hindex,int milliseconds, double& milliseconds2)
        {
            sprintf(line, "%i:%ia ",hindex, milliseconds); imprime(line);
            this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
            milliseconds2 = milliseconds * 1000;
            sprintf(line, "%i:%ib ", hindex, milliseconds); imprime(line);
            tareas_acabadas++;  th_end[hindex] = true;
        }
        int wait_first();
        void imprime(char* str) { if (FLAG_IMPRIME) { mtx.lock(); cout << str; mtx.unlock(); } }
    
    public:
        void lanzatareas();
    
    
        vector <future<void>> futures;
        int wait_first_future();
        void lanzatareas_future();//usa future
    };
    
    int main()
    {
        c_trim trim;
        timer();
        trim.lanzatareas();
        cout << endl;
        timer("4000 tareas using THREAD+ATOMIC:", 4000);
        trim.lanzatareas_future();
        cout << endl;
        timer("4000 tareas using FUTURE:", 4000);
        cout << endl << "Tareas acabadas:" << tareas_acabadas << endl;
        cout << "=== END ===\n"; (void)getchar();
    }
    
    void c_trim::lanzatareas()
    {
        th_index = 0;
        tareas_acabadas = 0;
        milliseconds_commanded = 0;
        double *timeout=new double[MAX_THREADS];
        int i;
        for (i = 0; i < MAX_THREADS; i++)
        {
            th_end[i] = true;
            th_result[i] = timeout[i] = -1;
        }
    
    
        for (i = 0; i < 4000; i++)
        {
            int milliseconds = 5 + (i % 10) * 2;
            {
                int j = wait_first();
                if (th[j].joinable())
                {
                    th[j].join();
                    th_result[j] = timeout[j];
                }
                milliseconds_commanded += milliseconds;
                th_end[j] = false;
                th[j] = thread(&c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
            }
        }
        for (int j = 0; j < MAX_THREADS; j++)
            if (th[j].joinable())
            {
                th[j].join();
                th_result[j] = timeout[j];
            }
    
        delete[] timeout;
        cout <<endl<< "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
    }
    
    void c_trim::lanzatareas_future()
    {
        futures.clear();
        futures.resize(MAX_THREADS);
    
        tareas_acabadas = 0;
        milliseconds_commanded = 0;
        double* timeout = new double[MAX_THREADS];
        int i;
        for (i = 0; i < MAX_THREADS; i++)
        {
            th_result[i] = timeout[i] = -1;
        }
    
    
        for (i = 0; i < 4000; i++)
        {
            int milliseconds = 5 + (i % 10) * 2;
            {
                int j;
                if (i < MAX_THREADS) j = i;
                else
                {
                    j = wait_first_future();
                    futures[j].get();
                    th_result[j] = timeout[j];
                }
                milliseconds_commanded += milliseconds;
                futures[j] = std::async(std::launch::async, &c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
            }
        }
        //Last MAX_THREADS:
        for (int j = 0; j < MAX_THREADS; j++)
        {
            futures[j].get();
            th_result[j] = timeout[j];
        }
    
        delete[] timeout;
        cout << endl << "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
    }
    
    int c_trim::wait_first()
    {
        int i;
        while (1)
            for (i = 0; i < MAX_THREADS; i++)
            {
                if (th_end[i] == true)
                {
                    return i;
                }
            }
    }
    
    
    
    //Espera que acabe algun future y da su index
    int c_trim::wait_first_future()
    {
        int i;
        std::future_status status;
        while (1)
            for (i = 0; i < MAX_THREADS; i++)
            {
                status = futures[i].wait_for(0ms);
                if (status == std::future_status::ready)
                    return i;
            }
    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-12
      • 2015-07-12
      • 1970-01-01
      • 1970-01-01
      • 2011-03-07
      • 1970-01-01
      相关资源
      最近更新 更多