【问题标题】:How to let std::thread delete the object automatically after excuting its member function如何让std :: thread在执行其成员函数后自动删除对象
【发布时间】:2019-09-13 06:18:16
【问题描述】:

我想实现一个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;
}

在cmets中,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

关闭

我弄错了test,实际上线程结束后对象被删除了。下面的test加上一个额外的成员变量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

但正如一些好心人所说,这不是一个好的设计,请尽量避免。

【问题讨论】:

  • “但不起作用” ...继续,告诉我们血淋淋的细节。发生了什么?如果Cmd *p = new Cmd(); delete p; p-&gt;ok() 打印了您的信息,您会感到惊讶吗?
  • 如果p-&gt;ok()在你的情况下打印我的消息,我会感到惊讶,至少它是UB。
  • 我希望 thread 在我的情况下删除 cmd
  • 您正在测试 UB。但是如果你有 UB,你就不能依赖你的测试。在已删除对象上调用成员函数是 UB,不需要崩溃。 ok() 正在努力工作。
  • 一个对象自发地窃取它自己的所有权然后结束它自己的生命周期是非常不寻常的。这种类型很容易使用不正确,使设计避免。好的软件设计的基本规则之一是使代码易于正确使用,而难以正确使用。

标签: c++ memory-management stdthread


【解决方案1】:

您可以使用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);
    }
}

【讨论】:

    【解决方案2】:

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

    1. 在客户端代码(主函数)中,你应该使用一些像unique_pointer这样的智能指针,这样一旦对象超出范围,它就会自动在unique_poiter析构函数中释放

      李>
    2. 您可以创建自己的smart pointer,它将是 Cmd 类的一种包装器。并且必须重载一些运算符。这个智能指针也将处理析构函数中动态分配的内存。客户端代码在动态创建 Cmd 对象时应该使用这个智能指针。

    【讨论】:

    • 客户端代码不知道 cmd 何时结束,unique_ptror 自动变量会提前删除对象,导致未定义行为。
    • @maidamai std::move()std::unique_ptr 改成std::thread,或者改用std::shared_ptr
    • @maidamai 当对象被删除时,你会得到什么未定义的行为?类的成员函数调用delete this 完全没问题,只要它在这样做后不触及其this 指针。它允许一种“最后一个关灯”的政策。您可以删除该对象并继续在您的方法中执行代码。只是在删除this后不要调用虚方法或访问成员变量。你应该没事。
    • @Wyck cmd thread 需要访问一些成员变量(我没有在示例代码中说明),但是如果您使用 unique_ptr 的自动变量,例如 EDIT,则整个对象已被删除参与我的帖子
    • @RemyLebeau 好像没有std::move() 对象还是被删除了,我测试出错了。
    猜你喜欢
    • 2015-12-19
    • 2013-07-26
    • 2017-08-29
    • 2018-01-24
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    • 1970-01-01
    • 2014-01-10
    相关资源
    最近更新 更多