signal senden und direkt deleteLater ausführen?

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

signal senden und direkt deleteLater ausführen?

Beitrag von hilefoks » 19. Juni 2013 19:56

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 » 20. Juni 2013 07:36

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: 141
Registriert: 13. März 2008 17:09

Re: signal senden und direkt deleteLater ausführen?

Beitrag von hilefoks » 3. Juli 2013 14:48

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: 141
Registriert: 13. März 2008 17:09

Re: signal senden und direkt deleteLater ausführen?

Beitrag von hilefoks » 3. Juli 2013 15:06

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 » 19. August 2013 19:45

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