QThread und neue Objekte erzeugen

Alles rund um die Programmierung mit Qt
BartSimpson
Beiträge: 1379
Registriert: 6. November 2004 12:03
Kontaktdaten:

QThread und neue Objekte erzeugen

Beitrag von BartSimpson »

Hallo ich versuche in einem Thread neue Objekte zu erzeugen aber es scheitert immer mit dem Fehler:
QObject: Cannot create children for a parent that is in a different thread.
QObject::startTimer: timers cannot be started from another thread
Jetzt habe ich schon gesucht aber noch nichts gefunden woran es liegen kann.
Hier mal der Code:

Code: Alles auswählen

run()
{
    K_Taktgeber=new QTimer();
    K_Netzwerkverwaltung=new QNetworkAccessManager();
 connect(K_Taktgeber,SIGNAL(timeout()),this,SLOT(aktualisieren()));
   connect(K_Netzwerkverwaltung,SIGNAL(finished(QNetworkReply*)),this,SLOT(NetzantwortErhalten(QNetworkReply*)));
    K_Taktgeber->start(K_Parameter->StreamaktualisierungsintervallHolen()*1000);
    exec();
}
Im DK werden dann die K_Taktgeber und K_Netzwerkverwaltung Objekte wieder gelöscht.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Irgendwie passen deine Meldungen nicht auf den Sourcecode. Versuche mal herauszufinden, wo (welche Zeile Code) welche Warning erzeugt.. Z.B. mit qDebug()-Ausgaben. Sicher falsch ist jedoch:
Im DK werden dann die K_Taktgeber und K_Netzwerkverwaltung Objekte wieder gelöscht.
Der Destruktor wird in einem anderen Thread-Kontext ausgeführt. Alle Objekte, welche du im Thread erzeugst, solltest du auch im Thread zerstören.. z.B. mit einem "shutdown()"-Signal oder auch einfach beim Verlassen des Threads nach "exec()".
BartSimpson
Beiträge: 1379
Registriert: 6. November 2004 12:03
Kontaktdaten:

Beitrag von BartSimpson »

Soviel konnte ich noch rausfinden,
wenn ich in dem Slot den der Timer aufruft,
auf das K_Netzwerkverwaltung Objekt zugreife, erscheinen die Fehlermeldungen.
Nur mache ich in dem Slot nichts mit dem QTimer Objekt.
lepsai
Beiträge: 573
Registriert: 14. September 2004 21:33
Wohnort: Berlin
Kontaktdaten:

Beitrag von lepsai »

Naja, die Idee diesen Timer und Objekte innerhalb des Threads zu definieren und dann noch exec() innerhalb von run() zu starten nicht gut.

Packe das doch in die Methoden der Klasse, die diese Thread-Logik verwaltet. Also ausserhalb von run(). Dort verbinest du auch die Signale.
In Qt ist die Erzeugung von Meta-Konstrukten ausserhalb von Main-Thread eben nicht erlaubt (aber auch nicht notwendig).

Und überlege dir noch mal die Logik, die du letzendlich mit dieser Implementierung erreichen willst. Ich glaube, es sind da einige Sachen, die sich vereinfachen lassen...
BartSimpson
Beiträge: 1379
Registriert: 6. November 2004 12:03
Kontaktdaten:

Beitrag von BartSimpson »

Na exec muss ich ja aufrufen, damit das mit den Signalen und Slots geht.
Das haben die in der Doku ja auch geschrieben.
Ich denke ich habe es jetzt.
Die Klasse die den Thread aufruft, warten das Signal started ab und ruft dann in dem Thread eine Funkion auf, die die Objekte erzeugt. Dann scheint es zu gehen.
lepsai
Beiträge: 573
Registriert: 14. September 2004 21:33
Wohnort: Berlin
Kontaktdaten:

Beitrag von lepsai »

Ok, das bedeutet, dass dieses ganzes Spiel außerhalb des Threads stattfindet. Der Thread läuft innerhalb der run() funktion ab. Alle anderen Methoden der Klasse werden im Main-Thread aufgerufen...
BartSimpson
Beiträge: 1379
Registriert: 6. November 2004 12:03
Kontaktdaten:

Beitrag von BartSimpson »

Die Objekte werden doch in der Klasse des Threads erzeugt. Dann solten die doch auch im Thread laufen
lepsai
Beiträge: 573
Registriert: 14. September 2004 21:33
Wohnort: Berlin
Kontaktdaten:

Beitrag von lepsai »

Nein, genau diese Annahme ist falsch. Der thread wird als Callback auf System-API eben implementiert. Dieses callback ist im Fall von QThread the run()-Funktion. Die Klasse (und ihre restlichen Methoden) hat damit nix zu tun...
BartSimpson
Beiträge: 1379
Registriert: 6. November 2004 12:03
Kontaktdaten:

Beitrag von BartSimpson »

Oh das ist interissant. Steht das irgenwo beschrieben?
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

lepsai hat geschrieben:Ok, das bedeutet, dass dieses ganzes Spiel außerhalb des Threads stattfindet. Der Thread läuft innerhalb der run() funktion ab. Alle anderen Methoden der Klasse werden im Main-Thread aufgerufen...
Das ist selbstverständlich völliger Quatsch... Ein Slot aufgerufen von exec() in der Run-Methode wird selbstverständlich auch im Threadkontext aufgerufen.
BartSimpson hat geschrieben:Die Objekte werden doch in der Klasse des Threads erzeugt. Dann solten die doch auch im Thread laufen
Exakt (sofern du damit "in der run-Methode" meinst.. im CTor wäre falsch..)
lepsai hat geschrieben:...Dieses callback ist im Fall von QThread the run()-Funktion. Die Klasse (und ihre restlichen Methoden) hat damit nix zu tun...
Das stimmt schon, aber wenn die Klassenmethoden (und damit auch die Slots) aus der Run-Methode aufgerufen werden, werden sie damit auch im Thread-Kontext ausgeführt!
RavenIV
Beiträge: 267
Registriert: 21. Januar 2009 14:24
Wohnort: Waldshut

Beitrag von RavenIV »

solarix hat geschrieben:
lepsai hat geschrieben:Ok, das bedeutet, dass dieses ganzes Spiel außerhalb des Threads stattfindet. Der Thread läuft innerhalb der run() funktion ab. Alle anderen Methoden der Klasse werden im Main-Thread aufgerufen...
Das ist selbstverständlich völliger Quatsch... Ein Slot aufgerufen von exec() in der Run-Methode wird selbstverständlich auch im Threadkontext aufgerufen.
BartSimpson hat geschrieben:Die Objekte werden doch in der Klasse des Threads erzeugt. Dann solten die doch auch im Thread laufen
Exakt (sofern du damit "in der run-Methode" meinst.. im CTor wäre falsch..)
lepsai hat geschrieben:...Dieses callback ist im Fall von QThread the run()-Funktion. Die Klasse (und ihre restlichen Methoden) hat damit nix zu tun...
Das stimmt schon, aber wenn die Klassenmethoden (und damit auch die Slots) aus der Run-Methode aufgerufen werden, werden sie damit auch im Thread-Kontext ausgeführt!
Sorry solarix,
aber Du hast vermutlich nicht verstanden, was lepsai sagen wollte.
Werden Methoden einer "Thread-Klasse" von ausserhalb (also nicht run()) aufgerufen, laufen sie nicht im Kontext des Thread ab, sondern im Kontext des aufrufenden Threads.
Linux, das längste Text-Adventure aller Zeiten
BartSimpson
Beiträge: 1379
Registriert: 6. November 2004 12:03
Kontaktdaten:

Beitrag von BartSimpson »

Ich merke schon das wird komplizierter als gedacht. Nagut nochmal zurück zum Reißbrett.
lepsai
Beiträge: 573
Registriert: 14. September 2004 21:33
Wohnort: Berlin
Kontaktdaten:

Beitrag von lepsai »

oh, jetzt wird es etwas hitzig... Sehr schön :)

Also schaffen wir Klarheit:

1. Ein Thread ist letztendlich eine, in einem bestimmten Kontext ausgeführte, Funktion.

Code: Alles auswählen

   d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start, this, CREATE_SUSPENDED, &(d->id));
Das bedeutet, dass das Objekt der Klasse, die von QThread abgeleitet ist, repräsentiert nicht den Thread und lebt selbst in dem Thread in dem es instanziert war. Nach dem Aufruf von QThread::start() wird die run()-Methode gestartet und NUR diese läuft im neuen Thread ab. Selbstverständlich, alle Methoden, die man innerhalb der run()-Funktion aufruft, werden im entsprechenden Thread laufen. Auch alle in der run() instanzierten Objekte leben im Specherberech dieses Threads.
2. Object-Model. Interessanter wird es bei den Signalen und Slots. Hier muss man unterscheiden zwischen folgenden Situationen für Triggerung von Signalen in der run():
a) Direkter Aufruf von Slots ist ein gewöhnlicher Funktionsaufruf, erfolgt also in dem run()-Thread
b) Lebt der Empfänder des Signals in der run() so wird sein Slot bei AutoConnection direkt im run()-Thread aufgerufen. Dasselbe passiert bei DirectConnection.
c) Die Slots die über QueuedConnection oder BlockedQueuedConnection getriggert werden, werden per Event-Mechanismus aufgelöst und sind letztendlich in QEventLoop aufgerufen, normalerweise in dem Haupt-Thread der Anwendung.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Code: Alles auswählen

connect(K_Netzwerkverwaltung,SIGNAL(finished(QNetworkReply*)),this,SLOT(NetzantwortErhalten(QNetworkReply*))); 
Hier könnte ggf. die AutoConnection nicht so funktionieren wie Du denkst. Bin mir aber nicht sicher.
Dein instanziertes Thread-Objekt wurde im Hauptthread erzeugt. D.h. es wird durch die AutoConnection eine Queued-Connection und Du landest bei der Abarbeitung im Hauptthread. Wenn Du dann von dort aus auf die Objekte im neuen Thread zugreifst kommt die Qt-Warnung.
Also entweder eine DirectConnection oder ein moveToThread() für das instanziierte Thread-Objekt.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
BartSimpson
Beiträge: 1379
Registriert: 6. November 2004 12:03
Kontaktdaten:

Beitrag von BartSimpson »

Hm jetzt habe ich das wieder so geändert,
das sie Objekte unter run erzeugt werden.
und nach exec gelöscht werden.
Und schon hagelt es wieder die Fehlermeldungen:
QObject::killTimer: timers cannot be stopped from another thread
QObject::startTimer: timers cannot be started from another thread
QObject::killTimer: timers cannot be stopped from another thread
QObject::startTimer: timers cannot be started from another thread
QObject: Cannot create children for a parent that is in a different thread.
Anbei habe ich mal die ganze Klasse.
Dateianhänge
Demo.7z
(1.65 KiB) 234-mal heruntergeladen
Antworten