如何检查 astd::thread
是否仍在运行(以独立于平台的方式)?它缺乏一种timed_join()
方法,joinable()
并不意味着这样做。
我想过在线程中用 a 锁定一个互斥锁,std::lock_guard
并使用try_lock()
互斥锁的方法来确定它是否仍然被锁定(线程正在运行),但这对我来说似乎不必要地复杂。
你知道更优雅的方法吗?
更新:要明确:我想检查线程是否干净退出。为此目的,“挂起”线程被视为正在运行。
如何检查 astd::thread
是否仍在运行(以独立于平台的方式)?它缺乏一种timed_join()
方法,joinable()
并不意味着这样做。
我想过在线程中用 a 锁定一个互斥锁,std::lock_guard
并使用try_lock()
互斥锁的方法来确定它是否仍然被锁定(线程正在运行),但这对我来说似乎不必要地复杂。
你知道更优雅的方法吗?
更新:要明确:我想检查线程是否干净退出。为此目的,“挂起”线程被视为正在运行。
如果您愿意使用 C++11std::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
forstd::thread
用于比 using 更清洁的解决方案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.
}
一个简单的解决方案是有一个布尔变量,线程定期将其设置为 true,并由想要知道状态的线程检查并设置为 false。如果变量长时间为假,则线程不再被认为是活动的。
更线程安全的方法是让子线程增加一个计数器,并且主线程将计数器与存储的值进行比较,如果在太长时间后相同,则认为子线程不活动。
但是请注意,在 C++11 中无法真正杀死或删除已挂起的线程。
编辑如何检查线程是否干净退出:基本上与第一段中描述的技术相同;将布尔变量初始化为 false。子线程做的最后一件事是将其设置为 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;
}
您始终可以检查线程的 id 是否不同于 std::thread::id() 默认构造的。一个正在运行的线程总是有一个真正的关联 id。尽量避免太多花哨的东西:)
这种简单的机制可用于检测线程的完成,而不会阻塞连接方法。
std::thread thread([&thread]() {
sleep(3);
thread.detach();
});
while(thread.joinable())
sleep(1);
创建一个运行线程和调用线程都可以访问的互斥锁。当正在运行的线程启动时,它锁定互斥锁,当它结束时,它解锁互斥锁。要检查线程是否仍在运行,调用线程调用 mutex.try_lock()。它的返回值是线程的状态。(如果 try_lock 有效,请确保解锁互斥锁)
一个小问题,mutex.try_lock() 将在创建线程和锁定互斥锁之间返回 false,但可以使用稍微复杂的方法来避免这种情况。
肯定有一个已初始化为 的互斥体包装变量false
,线程将其设置true
为它在退出之前所做的最后一件事。原子性足以满足您的需求吗?
我检查了两个系统: - 使用线程+原子:花费 9738 毫秒 -使用未来 + 异步:花费 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;
}
}