Seite 1 von 1
Signals und Objektverwaltung
Verfasst: 20. Dezember 2007 18:29
von patrikD
Hallo zusammen,
ich hab eine Frage zu Signals/Slots. Folgende Situation: ich habe ein Objekt welches ein Signal mit einem Objekt verschickt.
z.b.:
Code: Alles auswählen
class myClass: public QObject
{
signals:
void newEvent(OwnClass* event);
public:
void doSomeThing()
{
....
OwnClass* bla = new OwnClass;
emit newEvent(bla);
}
}
Dieses Signal kann ja an verschiedene Empfänger gehen u.a. an welche die sich in einem anderen Threadkontext befinden und das Signal dann dort nur in die EventQueue gepostet wird. Also wird das Signal emitted und ich kann nicht davon ausgehen das alle Empfänger dieses Signal bereits abgearbeitet haben. Ergo kann ich das Objekt auch nicht nach dem emit frei geben.
Ich muss das Objekt aber mit seinem Zeiger übergeben, da es nur die Basisklasse ist und die Empfänger unterschiedliche Ableitungen dieser Klasse erhalten.
Hat jemand eine Idee wie ich dem Herr werde und nicht massig Speicherlecks erzeuge?
danke
patrik
Verfasst: 20. Dezember 2007 19:16
von marco
Hallo,
nun, ich weiß nicht wieviele Ableitungen/Nachfahren deine Basisklasse besitzt, aber ich würde einfach ein eigenes Signal für jede Klasse erstellen:
Code: Alles auswählen
signals:
void signal1(OwnClassA ownClass);
void signal2(OwnClassB ownClass);
//...
Re: Signals und Objektverwaltung
Verfasst: 20. Dezember 2007 20:23
von solarix
patrikD hat geschrieben:
Ich muss das Objekt aber mit seinem Zeiger übergeben, da es nur die Basisklasse ist und die Empfänger unterschiedliche Ableitungen dieser Klasse erhalten.
Hat jemand eine Idee wie ich dem Herr werde und nicht massig Speicherlecks erzeuge?
hm.. wie wärs mit einem smart-pointer.. z.B. std::auto_ptr? Solange du deine "OwnClass" nicht von QObject ableitest und in die Obhut eines Parents gibst (=new OwnClass(this)) sollte das keine Probleme bereiten..
Verfasst: 20. Dezember 2007 21:02
von RD1978
Hallo,
leite OwnClass von QObject ab und ruf nach dem emit die Methode deleteLater() auf, dann wird die Instanz durch Qt gelöscht, wenn sie die Arbeit beendet hat.
The object will be deleted when control returns to the event loop.
Bsp.:
Code: Alles auswählen
class myClass: public QObject
{
signals:
void newEvent(OwnClass* event);
public:
void doSomeThing()
{
....
OwnClass* bla = new OwnClass;
emit newEvent(bla);
bla->deleteLater();
}
}
MfG RD1978
Verfasst: 20. Dezember 2007 21:22
von solarix
RD1978 hat geschrieben:
leite OwnClass von QObject ab und ruf nach dem emit die Methode deleteLater() auf, dann wird die Instanz durch Qt gelöscht, wenn sie die Arbeit beendet hat.
Ich habe keine Erfahrung mit deleteLayer() .. die doku interpretiere ich aber so, dass das Objekt beim zurückkehren in den Eventloop (pro Thread ein Eventloop..) zerstört wird. Was aber, wenn der empfangende Thread das Event (Signal mit dem Pointer) noch gar nicht abgearbeitet hat (anderer Eventloop)?
Verfasst: 21. Dezember 2007 09:18
von patrikD
Hallo zusammen,
erstmal danke für das ausgiebige Feedback

@marco: leider hab ich zuviele unterschiedliche Events mit unterschiedlichem Datengehalt. Für jedes Event signals und slots zu definieren wäre sehr umständlich.
@rd1978: solarix hat recht, das delete later funktioniert leider nur in der selben Eventloop.
@solarix: hab noch nichts mit den smartpointern gemacht. Funktionieren die über threads hinweg? Wisst ihr wie qt Zeiger in signals/slots behandelt? Wenn ich direkt Objekte übergebe werden diese ja für jeden Empfänger kopiert, bzw. eine eigene Instanz erstellt. Wie sieht das bei den Zeigern aus?
danke
patrik
Verfasst: 21. Dezember 2007 10:50
von solarix
Wisst ihr wie qt Zeiger in signals/slots behandelt? Wenn ich direkt Objekte übergebe werden diese ja für jeden Empfänger kopiert, bzw. eine eigene Instanz erstellt. Wie sieht das bei den Zeigern aus?
Hinter Signal/Slots ist nicht viel Magic.. "Call by Value" erzeugt einfach ne Kopie des Objektes, "Call by Address" kopiert den Pointer (Addresse). Bei Smartpointers wird damit also der Smartpointer (der als Value, nicht als Pointer uebergeben wird) komplett kopiert.
Trotzdem bin ich unsicher geworden.. auto_ptr scheint nicht thread-safe zu sein (code in der STL). Daher hast du folgende Optionen:
1. du schreibst ein eigener, thread-sicherer smartpointer, oder
2. du nimmst "shared_ptr" der "boost"-Library, oder
3. du uebergibst dem Signal normale Pointer, schreibst jedoch eine thread-sichere Pointerverwaltung (kleine Singleton-Klasse welche Referencen auf QObject* zaehlt und bei 0 den Speicher freigibt.. nicht viel Code..).
IMHO bietet da Qt keine Loesung (QSharedDataPointer scheint auch nicht das gewuenschte zu sein).. eigentlich schade..
Verfasst: 21. Dezember 2007 12:40
von patrikD
Hallo Solarix,
danke für die Antwort. Wird mir wohl noch ein wenig Kopfzerbrechen bereiten

Werd auf jedenfall die Lösung posten, wenn ich eine finde...
lg
Verfasst: 21. Dezember 2007 13:35
von FaS
Vielleicht nicht die eleganteste Lösung, aber zumindest recht einfach:
Du erstellst dir einfach (z.B. direkt in deinem MyClass-Objekt (oder woanders etwas globaler)) eine Liste aller versendeten Objekte zusammen mit der Zahl der Sendungen (quasi-Referenzzähler):
Code: Alles auswählen
QHash<OwnClass*,int> sentObjects;
// Verschicken eines Objektes:
OwnClass* bla = new OwnClass;
sentObjects[bla]++;
emit newEvent(bla);
Beim Empfänger schickst du den Zeiger einfach wieder zurück, sobald er nicht mehr benötigt wird:
Code: Alles auswählen
// Zurückschicken des Objektes:
emit canBeDeletedSignal(bla);
// Objekt mit den sentObjects
MyClass::canBeDeletedSlot(OwnClass* object)
{
// reference counter runterzählen und Objekt ggf. löschen
if( sentObjects[object]-- == 0 )
{
sentObjects.remove(object);
delete object;
}
}
Edit: scheint ja fast solarix' Option Nr. 3 zu entsprechen, nur etwas spezieller...
MfG,
FaS
Verfasst: 21. Dezember 2007 16:51
von patrikD
Hallo FaS,
danke, aber leider würde dann jedes Signal bei einer Kommunikation über Thread-Grenzen für einen doppelten Kontextwechsel sorgen und so sich negativ auf die Performance auswirken

Gibt es eine Möglichkeit zur Laufzeit die Anzahl der Connections zu einem Signal abzufragen?
Lg
Verfasst: 21. Dezember 2007 18:26
von upsala
Verfasst: 27. Dezember 2007 16:17
von patrikD
Hi,
danke Upsala

Habs mal damit versucht:
Das Objekt speichert beim emitten die Anzahl der Empfänger. Jeder Empfänger decrementiert den Wert und bei 0 wirft sich das Objekt dann weg. Is nicht sonderlich elegant, aber leichtgewichtig und threadsicher.
Hab die Sourcen in den Anhang gepackt. Für Verbesserungsvorschläge bin ich immer dankbar
MfG