通常,如果您有一个继承自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
对象成为堆栈分配对象,因为新线程需要它。另外,我已将Task
类private
发送到Timer
类,以防止其他人使用它。
我特别担心,因为删除Task
对象意味着删除基础Thread
对象。 Thread
对象中唯一的状态是pthread_t
变量。有什么方法可以回来咬我吗?请注意,pthread_t
方法完成后我不会使用run
变量。
我可以通过引入某种状态(通过delete this
方法的参数或Thread::start
构造函数中的某个东西)来绕过调用Thread
,表示分叉到的方法应该delete
它正在调用run
方法的对象。但是,代码似乎按原样运行。
有什么想法吗?
答案 0 :(得分:3)
我认为'删除这个'是安全的,只要你之后在run()方法中不做任何其他事情(因为所有Task的对象的成员变量等都将在那时释放内存)
我确实对你的设计感到疑惑......你是否真的想在每次有人安排定时器回调时产生一个新线程?这对我来说似乎效率低下。您可能会考虑使用线程池(或者甚至只是一个持久性计时器线程,它实际上只是一个大小为1的线程池),至少作为以后的优化。 (或者更好的是,实现计时器功能而不产生额外的线程...如果你正在使用具有超时功能的事件循环(如select()或WaitForMultipleObjects()),则可以复用任意数量的独立单个线程的事件循环中的计时器事件)
答案 1 :(得分:2)
只要您确定delete this;
,就没有什么特别可怕的了:
其中第一个是困难的。你可以采取一些步骤(例如让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
,我认为这是方法应该是可行的,但其他人建议使用寿命较长的线程(或线程池)是值得考虑的。