可以使用“删除这个;”在一个继承自Thread类的对象上?

时间:2010-02-09 06:18:02

标签: c++ multithreading memory-management pthreads delete-operator

通常,如果您有一个继承自Thread类的类,并且您希望该类的实例在完成运行后自动解除分配,那么delete this是否可以?

具体例子:

在我的应用程序中,我有一个Timer类,其中一个static方法名为schedule。用户称之为:

Timer::schedule((void*)obj, &callbackFunction, 15); // call callbackFunction(obj) in 15 seconds

schedule方法创建一个Task对象(其目的与Java TimerTask对象类似)。 Task类是private类的Timer,并且继承自Thread类(使用pthreads实现)。因此schedule方法执行此操作:

Task *task = new Task(obj, callback, seconds);
task->start(); // fork a thread, and call the task's run method

Task构造函数保存参数以便在新线程中使用。在新线程中,调用任务的run方法,如下所示:

void Timer::Task::run() {
    Thread::sleep(this->seconds);
    this->callback(this->obj);
    delete this;
}

请注意,我无法使task对象成为堆栈分配对象,因为新线程需要它。另外,我已将Taskprivate发送到Timer类,以防止其他人使用它。

我特别担心,因为删除Task对象意味着删除基础Thread对象。 Thread对象中唯一的状态是pthread_t变量。有什么方法可以回来咬我吗?请注意,pthread_t方法完成后我不会使用run变量。

我可以通过引入某种状态(通过delete this方法的参数或Thread::start构造函数中的某个东西)来绕过调用Thread,表示分叉到的方法应该delete它正在调用run方法的对象。但是,代码似乎按原样运行。

有什么想法吗?

4 个答案:

答案 0 :(得分:3)

我认为'删除这个'是安全的,只要你之后在run()方法中不做任何其他事情(因为所有Task的对象的成员变量等都将在那时释放内存)

我确实对你的设计感到疑惑......你是否真的想在每次有人安排定时器回调时产生一个新线程?这对我来说似乎效率低下。您可能会考虑使用线程池(或者甚至只是一个持久性计时器线程,它实际上只是一个大小为1的线程池),至少作为以后的优化。 (或者更好的是,实现计时器功能而不产生额外的线程...如果你正在使用具有超时功能的事件循环(如select()或WaitForMultipleObjects()),则可以复用任意数量的独立单个线程的事件循环中的计时器事件)

答案 1 :(得分:2)

只要您确定delete this;,就没有什么特别可怕的了:

  1. 对象总是动态分配的,
  2. 没有对象的成员它被删除后使用。

其中第一个是困难的。你可以采取一些步骤(例如让ctor私有),但是如果有人努力的话,几乎你可以 的任何事情被绕过。

那就是说,你可能会因某种线程池而变得更好。它往往更有效和可扩展。

编辑:当我谈到被绕过时,我在考虑这样的代码:

class HeapOnly {
  private:
    HeapOnly () {} // Private Constructor.
    ~HeapOnly () {} // A Private, non-virtual destructor.
  public:
    static HeapOnly * instance () { return new HeapOnly(); }
    void destroy () { delete this; }  // Reclaim memory.
};

这和我们提供的保护一样好,但绕过它是微不足道的:

int main() { 
    char buffer[sizeof(HeapOnly)];

    HeapOnly *h = reinterpret_cast<HeapOnly *>(buffer);
    h->destroy(); // undefined behavior...
    return 0;
}

当它像这样直接时,这种情况非常明显。当它分散在一个更大的系统上时,(例如)一个对象工厂实际上生成了这些对象,而另一个地方的代码完全分配了内存等,跟踪它会变得更加困难。

我最初说“delete this;没有什么特别可怕的”,我坚持认为 - 我不会再回过头来说它不应该被使用。如果其他代码“与其他人不能很好地发挥作用,我试图警告可能出现的那种问题。”

答案 2 :(得分:1)

如果您对Task对象所做的只是new它,start它,然后是delete它,为什么还需要一个对象呢?为什么不简单地实现一个执行start所做的功能(减去对象创建和删除)?

答案 3 :(得分:1)

delete this释放你为线程显式分配的内存,但是OS或pthreads库分配的资源如何,例如线程的调用堆栈和内核线程/进程结构(如果适用) ?如果您从未致电pthread_join()pthread_detach()并且您从未设置detachstate,我认为您仍然有内存泄漏。

这还取决于您的Thread类的设计使用方式。如果它在析构函数中调用pthread_join(),那就是一个问题。

如果您使用pthread_detach()(您的Thread对象可能已经在进行此操作),并且在删除this之后注意不要取消引用this,我认为这是方法应该是可行的,但其他人建议使用寿命较长的线程(或线程池)是值得考虑的。