Ist es in Ordnung, verwenden Sie "delete this;" auf ein Objekt erbt von einer Thread-Klasse?
-
19-09-2019 - |
Frage
Im Allgemeinen, wenn Sie haben eine Klasse, die erbt von einer Thread
Klasse, und Sie Instanzen dieser Klasse automatisch freigegeben, nachdem Sie fertig sind, ist es in Ordnung, delete this
?
Konkretes Beispiel:
In meiner Anwendung habe ich eine Timer
Klasse mit einem static
Methode genannt schedule
.Benutzer nennen es so:
Timer::schedule((void*)obj, &callbackFunction, 15); // call callbackFunction(obj) in 15 seconds
Die schedule
Methode erzeugt ein Task
Objekt (was den gleichen Zweck wie eine Java-TimerTask-Objekts).Die Task
Klasse private
der Timer
Klasse und erbt von der Thread
Klasse (implementiert mit pthreads).So die schedule
Methode, die das tut:
Task *task = new Task(obj, callback, seconds);
task->start(); // fork a thread, and call the task's run method
Die Task
Konstruktor speichert die Argumente, die für den Einsatz in den neuen thread.In den neuen thread, der Aufgabe run
- Methode aufgerufen wird, die wie folgt aussieht:
void Timer::Task::run() {
Thread::sleep(this->seconds);
this->callback(this->obj);
delete this;
}
Beachten Sie, dass kann ich nicht machen, die task
Objekt ein stack-Objekt zugeordnet, da der neue thread braucht.Auch, das ich gemacht habe Task
Klasse private
der Timer
Klasse, andere daran zu hindern, es zu verwenden.
Ich bin besonders besorgt, weil das löschen der Task
Objekt bedeutet, dass das löschen der zugrunde liegenden Thread
Objekt.Der einzige Staat in der Thread
Objekt ist eine pthread_t
- variable.Gibt es eine Möglichkeit, das könnte wieder kommen, mich zu beissen?Beachten Sie, dass ich nicht mit dem pthread_t
Variablen nach den run
Methode abgeschlossen ist.
Ich konnte bypass aufrufen delete this
durch die Einführung eine Art von Staat (entweder durch ein argument für die Thread::start
Methode oder etwas in der Thread
Konstruktor) bedeutet, dass die Methode, mit der Gabel zu sollen delete
das Objekt, dass es ist das aufrufen der run
Methode auf.Aber der code scheint nicht zu funktionieren.
Irgendwelche Gedanken?
Lösung
Ich denke, die ‚diese löschen‘ ist sicher, solange Sie etwas nicht tun, sonst danach in der run () -Methode (weil alle von den Membervariablen des Task-Objekts, usw., wird Speicher an diesem Punkt befreit werden) .
obwohlich über Ihr Design wundere ... tun Sie jedesmal, wenn jemand plant ein Timer-Rückruf einen neuen Thread sein wollen Laichen wirklich? Das scheint ziemlich ineffizient zu mir. Sie aussehen könnten, in einem Thread-Pool (oder auch nur einen einzigen persistenten Timer-Thread, der eigentlich nur einen Thread-Pool der Größe einer ist), zumindest eine Optimierung für später. (Oder besser noch, implementieren die Timer-Funktionalität ohne Laichen auf alle zusätzlichen Threads ... wenn Sie eine Ereignisschleife mit einer Timeout-Funktion verwenden (wie select () oder WaitForMultipleObjects ()) ist es möglich, eine beliebige Anzahl unabhängiger multiplexen Timer-Ereignisse innerhalb einer Ereignisschleife des einzelnen Thread)
Andere Tipps
Es gibt nichts besonders schreckliches über delete this;
wie lange, wie Sie versichern, dass:
- das Objekt ist immer dynamisch zugewiesen werden, und
- kein Mitglied des Objekts überhaupt benutzt wird, nachdem es gelöscht.
Der erste von diesen ist das schwierig.Es gibt Schritte, die Sie ergreifen können (z.B.macht das Tor "privat"), die dabei helfen, aber fast alles, was Sie tun können umgangen werden, wenn jemand versucht, hart genug.
Das heißt, Sie wäre wahrscheinlich besser dran mit einer Art von thread-pool.Es neigt zu werden mehr effiziente und skalierbare.
Edit:Wenn ich darüber gesprochen wird umgangen, ich dachte, der code wie diese:
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.
};
Das ist ungefähr so gut an Schutz wie wir können, aber die Fortbewegung ist es trivial:
int main() {
char buffer[sizeof(HeapOnly)];
HeapOnly *h = reinterpret_cast<HeapOnly *>(buffer);
h->destroy(); // undefined behavior...
return 0;
}
Wenn es ist direkt, wie diese, diese situation ist ziemlich offensichtlich.Wenn es erstreckte sich über eine größere Anlage, mit (zum Beispiel) ein Objekt, Fabrik, eigentlich der Herstellung der Objekte und den code komplett woanders Zuteilung von Speicher, etc., es kann sehr viel schwieriger geworden, die Spur.
Ich ursprünglich sagte: "es gibt nichts besonders schreckliches über delete this;
"und ich stehe dazu-bin ich nicht wieder auf und sagen, es sollte nicht verwendet werden.Ich bin versucht, Sie zu warnen, über die Art von Problemen, die entstehen können, wenn andere code "spielt nicht gut mit anderen."
delete this
gibt den Speicher explizit zugewiesen haben für das Gewinde zu verwenden, aber was ist mit den von der OS oder pThreads Bibliothek zugewiesenen Ressourcen, wie der Call-Stack des Threads und Kernel-Thread / Prozessstruktur (falls zutreffend)? Wenn Sie rufen nie pthread_join()
oder pthread_detach()
und Sie nie die detachstate , ich glaube, Sie noch ein Speicherleck haben.
Es hängt auch davon ab, wie Ihre Thread
Klasse verwendet werden soll. Wenn es pthread_join()
in seinem destructor nennt, das ist ein Problem.
Wenn Sie pthread_detach()
(die Ihr Thread
Objekt vielleicht schon tun), und Sie sind vorsichtig, nicht zu dereferenzieren this
nach this
zu löschen, ich denke, dieser Ansatz praktikabel sein sollte, aber andere Vorschläge einen langlebigeren Thread zu verwenden (oder Threadpool) ist auch eine Überlegung wert.
Wenn alles, was Sie jemals mit einem Task
Objekt tun es new
ist, start
es, und es dann delete
, warum werden Sie brauchen, um ein Objekt für das überhaupt? Warum implementieren nicht einfach eine Funktion, die tut, was start
tut (minus Objekt Erstellen und Löschen)?