Seite 1 von 1

signal senden und direkt deleteLater ausführen?

Verfasst: 19. Juni 2013 19:56
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

Re: signal senden und direkt deleteLater ausführen?

Verfasst: 20. Juni 2013 07:36
von Christian81
deleteLater() wird erst ausgeführt, nachdem alle Signals dieses Objektes abgearbeitet wurden, also ist es sicher.

Re: signal senden und direkt deleteLater ausführen?

Verfasst: 3. Juli 2013 14:48
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?

Re: signal senden und direkt deleteLater ausführen?

Verfasst: 3. Juli 2013 15:06
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?

Re: signal senden und direkt deleteLater ausführen?

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