c++ - 如何让std :: thread在执行其成员函数后自动删除对象
问题描述
我想实现一个cmmand
在另一个线程中做一些工作的类,我不想让用户手动删除该对象。我的command
类是这样的:
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
关闭
我在测试方面犯了一个错误,实际上该对象在线程结束后被删除。通过以下带有附加成员变量的测试更清楚int i
。
#include <functional>
#include <iostream>
#include <stack>
#include <thread>
using namespace std;
class Cmd {
public:
~Cmd() { std::cout << "destructor" << std::endl; }
void excute() {
std::cout << i << " thread begins" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // do some work
std::cout << i << " thread ends" << std::endl;
}
void run() {
// I want std::unique_ptr to delete 'this' after work is done,but it seems
// not working
std::thread td(&Cmd::excute, std::unique_ptr<Cmd>(this));
td.detach();
}
// test if this object is still alive
void ok() { std::cout << i << " OK" << std::endl; }
int i;
};
int main() {
Cmd *p = new Cmd();
p->i = 10;
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;
}
flowwing 输出证明该对象已被删除。
10 thread begins
10 thread ends
destructor
-572662307 OK
但正如一些好心人所建议的那样,这不是一个好的设计,尽可能避免它。
解决方案
您可以使用std::future代替线程来表示状态。然后,您可以等待任务完成,或者完全忽略未来。
#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);
}
}
推荐阅读
- javascript - 从 html 文件中的外部 js 文件调用 Javascript 函数
- terraform - user_data 中的命令不会在 terraform 中执行
- javascript - 无效日期未在 IE 中返回为无效
- c# - C# Google Chrome Selenium URL 导航与 BinaryLocation
- javascript - Tern 函数参数:如何用键指定对象?
- android - 打开 OneSignal URL 到另一个活动的 WebView
- javascript - 将 html 表格导出到 XLSX 时如何设置单元格子项的样式
- regex - 使用正则表达式将字符串分成两组并仅显示最后一组
- erlang - erlang - 监视器将信息发送到 shell 而不是 gen_server
- c# - 从表单接受属性中排除自动完成文本框?