signal senden und direkt deleteLater ausführen?

Alles rund um die Programmierung mit Qt
Antworten
hilefoks
Beiträge: 144
Registriert: 13. März 2008 16:09

signal senden und direkt deleteLater ausführen?

Beitrag von hilefoks »

Moin,

angenommen, ich habe eine Klasse Foo die ein folgendes Signal besitzt:

Code: Alles auswählen

void finished(Foo *obj);
Das signal enthält also die Klasse selbst als Parameter, wobei meine Frage aber genauso gilt, wenn dies nicht der Fall wäre und stattdessen mit sender() auf das Objekt zugegriffen würde.

Folgende einfacher Klasse als Beispiel:

Code: Alles auswählen

class Foo : public QObject
{
   Q_OBJECT 
 
   public:
       Foo() { QTimer::singleShot( 0, this, SLOT( timerEvent() ) ); }
       QString message() { return "Hello World!"; }
        
   private slots:
       void timerEvent() { emit finished( this ); this->deleteLater(); }
        
   signals:
       void finished(Foo *obj);
};
Diese Klasse löscht sich mittels deleteLater() selbst, nachdem sie das finished Signal gesendet hat. Ein ganz einfacher Test zeigt mir auch, das ich nach dem finished Signal durchaus noch auf die message() Methode zugreifen darf:

Code: Alles auswählen

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Foo *foo = new Foo;
    QObject::connect( foo, &Foo::finished, [foo]() { qDebug() << foo->message(); } );
    
    return app.exec();
}
Meine Frage ist nun, ob das wirklich sicher ist oder unter welchen Umständen ich darauf verzichten sollte. Falls es mit diesem Konstrukt ein Problem gibt, welche alternative habe ich?

MfG,
Hilefoks
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: signal senden und direkt deleteLater ausführen?

Beitrag von Christian81 »

deleteLater() wird erst ausgeführt, nachdem alle Signals dieses Objektes abgearbeitet wurden, also ist es sicher.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
hilefoks
Beiträge: 144
Registriert: 13. März 2008 16:09

Re: signal senden und direkt deleteLater ausführen?

Beitrag von hilefoks »

Danke! Deine Aussage bestätigt meine Tests und entsprechendes habe ich in meiner Anwendung nun auch umgesetzt. Funktioniert soweit auch wunderbar, aber jetzt gibt es doch ein Problem.

Angenommen, ich habe eine Funktion um dieses Objekt zu erzeugen, diese Funktion verschiebt die Instanz aber zudem in einen anderen Thread:

Code: Alles auswählen

Foo* createFoo()
{
    QThread *thread = new QThread;
    Foo *f = new Foo;
    f->moveToThread( thread );

    connect( thread, &QThread::started, f, &Foo::execute );
    connect( f, &Foo::finished, thread, &QThread::quit );
    connect( thread, &QThread::finished, thread, &QThread::deleteLater );

    thread->start();

    return f;
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Foo *foo = createFoo();
    QObject::connect( foo, &Foo::finished, [foo]() { qDebug() << foo->message(); } );
    
    return app.exec();
}
Jetzt wird deleteLater() zu früh ausgeführt. Kann ich das verhindern?
hilefoks
Beiträge: 144
Registriert: 13. März 2008 16:09

Re: signal senden und direkt deleteLater ausführen?

Beitrag von hilefoks »

Ergänzung: Ich glaube, wenn ich deleteLater() aus der timerEvent Methode von Foo entferne und stattdessen in createFoo diese connections erstelle, das es dann geht:

Code: Alles auswählen

    connect( thread, &QThread::started, f, &CreateJob::execute );
    connect( f, &Foo::finished, thread, &QThread::quit );
    connect( thread, &QThread::finished, thread, &QThread::deleteLater );
    connect( thread, &QThread::destroyed, f, &Foo::deleteLater );
Das finished Signal vom Foo ist mit dem quit Slot von Thread verbunden. Das finished Signal vom Thread ist mit deleteLater vom Thread verbunden. Und letztlich ist das destroyed Signal vom Thread mit dem deleteLater Slot von Foo verbunden.

Ist das so korrekt und sicher?
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Re: signal senden und direkt deleteLater ausführen?

Beitrag von solarix »

Ist schon älter, aber bei Memory-Leaks kann ich nicht widerstehen:
hilefoks hat geschrieben: Ist das so korrekt und sicher?
Weder noch. Begründung: Wenn der Thread zerstört ist, kann er den Job nicht mehr löschen. -> Memory-Leak.

Lösung: zuerst den Job löschen, dann den Thread:

Code: Alles auswählen

QObject::connect( thread,SIGNAL(started()), f, SLOT(execute()));
QObject::connect( f,SIGNAL(finished()), f, SLOT(deleteLater()));
QObject::connect( f,SIGNAL(destroyed()), thread, SLOT(quit()));
QObject::connect( thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
Wenn der Job wegen den Job-Resultaten noch benötigt wird, darf er halt nicht gelöscht werden (sondern muss wieder an dern Auftraggeber zurückgesendet werden). Oder besser: er sendet sein Resultat mittels Signal an den Auftraggeber. Dann darf er getrost Selbstmord begehen.
Antworten