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

但正如某些人所建议的,这不是一个好的设计,请尽可能避免它。

有两种方法可以自动删除动态创建的内存

  • 在客户机代码(main函数)中,您应该使用一些类似于的智能指针,这样一旦对象超出范围,它就会在unique_poiter析构函数中自动释放

  • 您可以创建自己的,它将是Cmd类的包装器。必须让一些操作员超负荷工作。这个智能指针还将负责析构函数中动态分配的内存。客户端代码在动态创建Cmd对象时应使用此智能指针


  • 您可以使用来表示状态,而不是线程。然后,您可以等待任务完成,也可以完全忽略未来

    #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自动变量将删除对象,从而导致未定义的行为。@maidamai
    std::move()
    std::unique\u ptr
    插入
    std::thread
    ,或使用
    std::shared\u ptr
    instead@maidamai当对象被删除时,您会得到什么未定义的行为?类的成员函数调用
    delete this
    是完全可以的,前提是这样做后它不会碰到它的
    this
    指针。它允许一种“最后一个熄灭灯”的策略。您可以删除对象并继续在方法中执行代码。只需在删除
    后不要调用虚拟方法或访问成员变量。您应该没事。@Wyck
    cmd线程
    需要访问一些成员变量(我没有在示例代码中演示),但是如果在我的示例代码中使用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);
        }
    }