如何放弃或取消std::thread
How to abandon or cancel std::thread
我正在使用 std::threads 来实现一个生产者,它会做一些长时间的计算,但使用的代码没有提供中止方法。但是我需要调用线程在请求中止后立即继续。
所以我试图通过调用 detach() 来放弃线程。思路是线程完成计算,结果被丢弃:
#include <thread>
#include <iostream>
#include <queue>
#include <mutex>
class C
{
public:
void spawn()
{
t1.swap(std::thread(&C::producer, this, 1));
}
void abort()
{
std::lock_guard<std::mutex> lk(mx);
aborted = true;
t1.detach();
}
bool tryGetResult(int &result)
{
std::lock_guard<std::mutex> lk(mx);
if (!q.empty()) {
result = q.front();
q.pop();
return true;
}
else
return false;
}
private:
std::thread t1;
std::mutex mx;
std::queue<int> q;
bool aborted = false;
void producer(int id)
{
int i = 0;
while (true) {
std::lock_guard<std::mutex> lk(mx);
if (aborted) break;
q.push(id * 10 + i);
i++;
}
}
};
int main()
{
{
C c;
c.spawn();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
c.abort();
}
std::cout << "Done...";
std::cin.get();
}
这种方法似乎有效,但对我来说却很糟糕。
我不明白为什么线程不会导致访问冲突,因为它试图在对象被销毁后访问它。
有没有办法通知线程它已经分离并且必须在不访问任何 class 成员的情况下退出。
t1.swap(std::thread(&C::producer, this, 1));
上面是ill-formed,因为声明了std::thread::swap
:
void swap( thread& other ) noexcept;
std::thread(&C::producer, this, 1)
是一个临时值,因此是一个右值。它不能绑定到 swap
.
的 non-const 左值引用
也许您打算改写 t1 = std::thread(&C::producer, this, 1);
。
I don't understand why the thread does not cause an access violation
在其生命周期之外访问对象的行为未定义。不能保证会导致访问冲突。
Is this OK
没有
or is there a better way to implement something like this?
理想的解决方案视情况而定。
一个简单的解决方案是使用有状态可调用对象来创建线程,并存储指向共享状态的共享指针。谁活得更长,谁就可以让它活下去。如果经常访问状态,这可能会对性能产生影响。
更简单的解决方案是对共享状态使用静态存储。这没有共享指针可能存在的潜在性能问题,但全局状态存在其他问题。
你的线程明明是在你的class C被销毁后访问数据的。这是未定义的行为,所以不要依赖这个。
如果我的理解正确,您希望能够在退出程序之前终止线程。这本质上是不安全的,因为当您终止线程时您不知道线程正在访问什么内存,并且您的程序很可能会处于错误状态。
终止线程的唯一正确方法是关闭线程本身。您已经在中止功能中这样做了。这会将 aborted
变量设置为 true
,这反过来会破坏我们的线程循环并退出线程函数。
但是与其分离你正在做的事情,不如加入线程。这将使您的主线程等待,直到您的工作线程在您恢复之前终止。在此之后删除 C 是安全的。
正如接受的答案中所建议的,shared_ptr 可以提供所需的行为。
在 abort-case 中,shared_ptr 与最后一个废弃的线程一样长。
#include <thread>
#include <iostream>
#include <queue>
#include <mutex>
#include <memory>
class C
{
public:
~C()
{
std::cout << "Dtor\n";
}
void abort()
{
std::lock_guard<std::mutex> lk(mx);
aborted = true;
}
std::mutex mx;
std::queue<int> q;
bool aborted = false;
static void staticProducer(std::shared_ptr<C> arg, int id)
{
arg->producer(id);
}
void producer(int id)
{
int i = 0;
while (i<300000) {
std::lock_guard<std::mutex> lk(mx);
if (aborted) break;
q.push(id * 10 + i);
i++;
std::cout << "Push\n";
}
}
};
int main()
{
{
std::shared_ptr<C> c = std::make_shared<C>();
std::thread t1 = std::thread(&C::staticProducer, c, 1);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
// scenario 1: abort
c->abort();
t1.detach();
// scenario 2: wait for completion and get result
// t1.join();
// std::cout << c->q.size() << "\n";
}
std::cout << "Done...";
std::cin.get();
}
我正在使用 std::threads 来实现一个生产者,它会做一些长时间的计算,但使用的代码没有提供中止方法。但是我需要调用线程在请求中止后立即继续。 所以我试图通过调用 detach() 来放弃线程。思路是线程完成计算,结果被丢弃:
#include <thread>
#include <iostream>
#include <queue>
#include <mutex>
class C
{
public:
void spawn()
{
t1.swap(std::thread(&C::producer, this, 1));
}
void abort()
{
std::lock_guard<std::mutex> lk(mx);
aborted = true;
t1.detach();
}
bool tryGetResult(int &result)
{
std::lock_guard<std::mutex> lk(mx);
if (!q.empty()) {
result = q.front();
q.pop();
return true;
}
else
return false;
}
private:
std::thread t1;
std::mutex mx;
std::queue<int> q;
bool aborted = false;
void producer(int id)
{
int i = 0;
while (true) {
std::lock_guard<std::mutex> lk(mx);
if (aborted) break;
q.push(id * 10 + i);
i++;
}
}
};
int main()
{
{
C c;
c.spawn();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
c.abort();
}
std::cout << "Done...";
std::cin.get();
}
这种方法似乎有效,但对我来说却很糟糕。 我不明白为什么线程不会导致访问冲突,因为它试图在对象被销毁后访问它。
有没有办法通知线程它已经分离并且必须在不访问任何 class 成员的情况下退出。
t1.swap(std::thread(&C::producer, this, 1));
上面是ill-formed,因为声明了std::thread::swap
:
void swap( thread& other ) noexcept;
std::thread(&C::producer, this, 1)
是一个临时值,因此是一个右值。它不能绑定到 swap
.
也许您打算改写 t1 = std::thread(&C::producer, this, 1);
。
I don't understand why the thread does not cause an access violation
在其生命周期之外访问对象的行为未定义。不能保证会导致访问冲突。
Is this OK
没有
or is there a better way to implement something like this?
理想的解决方案视情况而定。
一个简单的解决方案是使用有状态可调用对象来创建线程,并存储指向共享状态的共享指针。谁活得更长,谁就可以让它活下去。如果经常访问状态,这可能会对性能产生影响。
更简单的解决方案是对共享状态使用静态存储。这没有共享指针可能存在的潜在性能问题,但全局状态存在其他问题。
你的线程明明是在你的class C被销毁后访问数据的。这是未定义的行为,所以不要依赖这个。
如果我的理解正确,您希望能够在退出程序之前终止线程。这本质上是不安全的,因为当您终止线程时您不知道线程正在访问什么内存,并且您的程序很可能会处于错误状态。
终止线程的唯一正确方法是关闭线程本身。您已经在中止功能中这样做了。这会将 aborted
变量设置为 true
,这反过来会破坏我们的线程循环并退出线程函数。
但是与其分离你正在做的事情,不如加入线程。这将使您的主线程等待,直到您的工作线程在您恢复之前终止。在此之后删除 C 是安全的。
正如接受的答案中所建议的,shared_ptr 可以提供所需的行为。 在 abort-case 中,shared_ptr 与最后一个废弃的线程一样长。
#include <thread>
#include <iostream>
#include <queue>
#include <mutex>
#include <memory>
class C
{
public:
~C()
{
std::cout << "Dtor\n";
}
void abort()
{
std::lock_guard<std::mutex> lk(mx);
aborted = true;
}
std::mutex mx;
std::queue<int> q;
bool aborted = false;
static void staticProducer(std::shared_ptr<C> arg, int id)
{
arg->producer(id);
}
void producer(int id)
{
int i = 0;
while (i<300000) {
std::lock_guard<std::mutex> lk(mx);
if (aborted) break;
q.push(id * 10 + i);
i++;
std::cout << "Push\n";
}
}
};
int main()
{
{
std::shared_ptr<C> c = std::make_shared<C>();
std::thread t1 = std::thread(&C::staticProducer, c, 1);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
// scenario 1: abort
c->abort();
t1.detach();
// scenario 2: wait for completion and get result
// t1.join();
// std::cout << c->q.size() << "\n";
}
std::cout << "Done...";
std::cin.get();
}