C++ 如何让std::thread在执行其成员函数后自动删除对象
我想实现一个C++ 如何让std::thread在执行其成员函数后自动删除对象,c++,memory-management,stdthread,C++,Memory Management,Stdthread,我想实现一个cmmand类,该类在另一个线程中执行一些工作,我不想让用户手动删除该对象。我的命令类如下: class Cmd { public: void excute() { std::cout << "thread begins" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(2)); // do some work std::cout << "
cmmand
类,该类在另一个线程中执行一些工作,我不想让用户手动删除该对象。我的命令
类如下:
class Cmd {
public:
void excute() {
std::cout << "thread begins" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // do some work
std::cout << "thread ends" << std::endl;
}
void run() {
// I want std::unique_ptr to delete 'this' after work is done,but does't work
std::thread td(&Cmd::excute, std::unique_ptr<Cmd>(this));
td.detach();
}
// test if this object is still alive
void ok() { std::cout << "OK" << std::endl; }
};
int main() {
Cmd *p = new Cmd();
p->run();
// waiting for cmd thread ends
std::this_thread::sleep_for(std::chrono::seconds(3));
p->ok(); // I thought p was deleted but not
return 0;
}
正如在注释中一样,cmd线程完成后对象仍然是活动的,我想知道如何实现这种功能
编辑
cmd
的用户不知道cmd
将在何时完成,因此流动的用例将导致UB
std::unique_ptr<Cmd> up(new Cmd); // or just Cmd c;
up->run();
// cmd will be deleted after out of scope but cmd::excute may still need it
flowwing输出证明该对象已被删除
10 thread begins
10 thread ends
destructor
-572662307 OK
但正如某些人所建议的,这不是一个好的设计,请尽可能避免它。有两种方法可以自动删除动态创建的内存
您可以使用来表示状态,而不是线程。然后,您可以等待任务完成,也可以完全忽略未来
#include <future>
#include <chrono>
#include <mutex>
#include <iostream>
class Cmd {
public:
std::future<void> run() {
std::lock_guard<std::mutex> lock(startMutex);
if (started) {
throw std::logic_error("already started");
}
started = true;
// Take copies here, so that it doesn't matter if Cmd is destroyed
int i_ = i;
return std::async(std::launch::async, [i_]() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << i_ << std::endl;
});
}
int i = 0;
private:
std::mutex startMutex;
bool started = false;
};
int main() {
auto p = std::make_unique<Cmd>();
p->i = 10;
auto f = p->run();
p.reset();
// Do some other work
// Wait for the task to finish (or use f.get() if there is no need to
// do some other work while waiting)
if (f.valid()) {
std::future_status operation;
do {
// Do some other work
operation = f.wait_for(std::chrono::milliseconds(1));
} while (operation != std::future_status::ready);
}
}
#包括
#包括
#包括
#包括
类Cmd{
公众:
std::future run(){
标准:锁和防护锁(startMutex);
如果(启动){
抛出std::逻辑_错误(“已启动”);
}
开始=真;
//在这里复制,这样Cmd是否被销毁就无关紧要了
int i_uui=i;
返回std::async(std::launch::async,[i_](){
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout run();
p、 重置();
//做一些其他的工作
//等待任务完成(如果不需要,则使用f.get()
//等待时做一些其他工作)
if(f.valid()){
std:未来状态操作;
做{
//做一些其他的工作
操作=f.wait_for(std::chrono::毫秒(1));
}while(操作!=std::future_status::ready);
}
}
“但不起作用”…继续,告诉我们血淋淋的细节。发生了什么事?如果Cmd*p=new Cmd();delete p;p->ok()
打印了你的消息,你会感到惊讶吗?如果p->ok(),我会感到惊讶
在您的案例中打印了我的消息,至少是UB。我希望线程
在我的案例中删除cmd
。您正在测试UB。但是如果您有UB,您不能依赖您的测试。对已删除对象调用成员函数是UB,不需要崩溃。ok()
正落在它的工作上。一个对象自发地窃取它自己的所有权,然后结束它自己的生命周期,这是极不寻常的。这种类型很容易被不正确地使用,这使得设计成为一种必须避免的。好的软件设计的基本规则之一是使代码易于正确使用,而不容易被错误地使用。客户端代码不知道是什么当cmd结束时,unique_ptror自动变量将删除对象,从而导致未定义的行为。@maidamaistd::move()
将std::unique\u ptr
插入std::thread
,或使用std::shared\u ptr
instead@maidamai当对象被删除时,您会得到什么未定义的行为?类的成员函数调用delete this
是完全可以的,前提是这样做后它不会碰到它的this
指针。它允许一种“最后一个熄灭灯”的策略。您可以删除对象并继续在方法中执行代码。只需在删除此
后不要调用虚拟方法或访问成员变量。您应该没事。@Wyckcmd线程
需要访问一些成员变量(我没有在示例代码中演示),但是如果在我的示例代码中使用unique_ptr的自动变量,如EDIT
part,则整个对象已被删除post@RemyLebeau似乎没有std::move()
该对象仍然被删除,我在测试中犯了一个错误。
#include <future>
#include <chrono>
#include <mutex>
#include <iostream>
class Cmd {
public:
std::future<void> run() {
std::lock_guard<std::mutex> lock(startMutex);
if (started) {
throw std::logic_error("already started");
}
started = true;
// Take copies here, so that it doesn't matter if Cmd is destroyed
int i_ = i;
return std::async(std::launch::async, [i_]() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << i_ << std::endl;
});
}
int i = 0;
private:
std::mutex startMutex;
bool started = false;
};
int main() {
auto p = std::make_unique<Cmd>();
p->i = 10;
auto f = p->run();
p.reset();
// Do some other work
// Wait for the task to finish (or use f.get() if there is no need to
// do some other work while waiting)
if (f.valid()) {
std::future_status operation;
do {
// Do some other work
operation = f.wait_for(std::chrono::milliseconds(1));
} while (operation != std::future_status::ready);
}
}