Seite 1 von 1

Frage zu QSemaphoren in QThreads

Verfasst: 7. Oktober 2009 15:07
von Apollinaris
Hallo,

ich habe eine Frage an euch. Eins vorab, ich benutze Visual Studio 2008 + QT 4.5. Ich möchte in einer Klasse die ich von QThread abgeleitet hab zwei semaphoren benutzen.

Die erste soll zum steuern meines Ablaufs sein in run().

Code: Alles auswählen

void run()
{
	// Thread-Init:
	connect( udpSocket, SIGNAL(readyRead() ), this, SLOT( getUdpPackage() ) );

	timer = new QTimer;
	connect( timer, SIGNAL( timeout() ), this,  SLOT( timeOut() ) );
	timer->setInterval( TIMEOUT );

	semaphore = new QSemaphore;

	workflowSemaphore = new QSemaphore;

	//Port zum horchen erzuegen
       udpSocket->bind( ... );

	while(1)
	{
		workflowSemaphore->acquire();

                *
                *
                *
	}

	udpSocket->close();
	delete udpSocket;
}
Irgendwelche methoden, die meine Klasse verfügt sollen die workflowSemaphore->release(); aufrufen, damit die eigentlich Aufgabe ausgeführt werden.

Ich möchte nach dem Senden prüfen, ob das was ich zurück bekommen okay ist oder nicht. Entsprechend über einen Rückgabewert true oder false meinen anderen Thread der GUI die diese Methode sendUdpPackage(); aufruft, drauf reagieren kann.

Code: Alles auswählen

bool sendUdpPackage()
{
	*
	*
	udpSocket->writeDatagram( ... );

	semaphore->acquire();

	// Empfangene Variable prüfen
	// Und entsprechenden Rückgabe Wert
	if ( Var  == OKAY)
		return true;
	else
		return false;
}

void getUdpPackage()
{
	*
	*
	udpSocket->readDatagram( ... );

	// Empfangene Variable besetzen
	Var = ... ;

        semaphore->release();
}
Dabei blockiert sich logischer Weise meine Gui, die diese Methode aufruft. Auch wenn es so funktioniert, finde ich es nicht schön gelöst. Kann mir da einer von einen Tipp geben, wie ich das besser lösen kann.

Danke
Gruss
Apollinaris

Verfasst: 7. Oktober 2009 21:05
von Apollinaris
Hat den keiner einen Rat für mich?

Verfasst: 7. Oktober 2009 21:16
von Christian81
Den Rückgabewert einfach erst später auswerten, nämlich über ein Signal.

Verfasst: 7. Oktober 2009 21:29
von Apollinaris
Die Idee gefällt mir. Ich weiß das man bei MS Visual Studio auf events warten kann. Nur gibt es eine Möglichkeit in QT auf Signale zuwarten? Oder verstehe ich deine Idee falsch?

Verfasst: 7. Oktober 2009 21:33
von Christian81
Warten im Hauptthread bedeutet immer einen Freeze. Du musst dem User schon was bieten - am Anfang hatte mal eine Sanduhr gereicht. Da ich aber nicht weiß warum du warten willst/musst...

Verfasst: 7. Oktober 2009 22:49
von Apollinaris
Ich will den Rückgabewert verarbeiten können. Wenn ein False kommt, soll der User aufgefordert werden zu reagieren. Wenn ein True kommt, soll sie die Software weiterhin selbst arbeiten und senden. Und egal was passiert, die GUI soll nicht einfrieren.

Verfasst: 8. Oktober 2009 06:30
von Christian81
Ich würde einen kleinen Wartedialog oder sowas einbauen wenn es wirklich nötig ist.

Verfasst: 8. Oktober 2009 10:03
von RHBaum
Ich will den Rückgabewert verarbeiten können. Wenn ein False kommt, soll der User aufgefordert werden zu reagieren.
Das in verbindung mit multithreading beisst sich bissi ....

Definier mal, die UI soll nicht einfrieren genauer, was du dir drunter vorstellst. Da steh ich aufn schlauch. Weil eiegntlich soll ja der User "nix machen" bis der rueckgabewert eintrifft ?
Willst du nur das das blockieren des Neuzeichnen der Widgets verhindert wird ? da gibts andere, einfachere mittel und wege.

Wenn doch richtiges MT willst, solltest du eh anfangen, asynchron zu denken. D.h. sowas in der Art wie:
- Aktion starten .... User bekommt die kontrolle sofort wieder zurueck.
- Optional kann deine Aktion Ihren Progressfortschritt in bestimmten intervallen an die GUI schicken.
- User kann in der zwischenzeit andere Aktionen starten ...
- Deine Aktion ist fertig geworden (im Hintergrund) und benachrichtigt dich (GUI Thread) drueber.
- Du fragst bei naechster gelegenheit das Resultat ab, und praesentierst dem User das Ergebnis, bzw bietest deine Auswahloptionen ....
- user startet durch die Auswahloption die naechste Aktion ...

So solltest du bei multithreading denken .... ansonsten kommst ganz schnell in Sackgassen.
Multithreading Programme unterscheiden sich meist schon vom aufbau der Dialogfelder und vom Handling gegenüber Singlethread Anwendungen. Also die wahl von Multithreading hat schon einen Einfluss auf die gestaltung der Ablaufe, den Aufbau der dialoge ... etc.

Noch paar Hinweise:
Fuer Sockets braucht man nicht zwangslaeufig Multithreading. Die Asynchronitaet kann einem das BS da abnehmen. Siehe Nicht blockierende Aufrufe. Weiss ned ob die QT das unterstuetzt ... aber ich denk scho.

Semaphoren sind die allgemeinere Variante von Mutexen. Mit semaphoren limitiert man die gleichzeitige benutzung einer ressource auf eine bestimmte Anzahl von Threads/Prozessen. Ist die Anzahl = 1, also eine ressource soll nur von einem Thread/Prozess gleichzeitig verwendet werden, dann ist das ein Mutex ! Mutexe sind aber spezieller, komfortabler und performanter als die Semaphore. Also gewoehn dir an fuer genau in dem Fall von einem Mutex zu sprechen und auch Mutexe aus den Bibs zu verwenden.

Brauchst du aber vielleicht gar nicht ....
Stell deinen socket ein ...
Connecte den Socket auf das readyRead() Signal.
Schick dein UDP packet raus ....
Mach nen eintrag in ner "Liste", das du die Anfrage rausgeschickt hasst.

bekommst du das Signal ....
Sieh dir die Daten des UDP sockets an ....
Schau mit welchen eintrag in der "Liste" das Ding matchen kann ... Entferne den Eintrag aus der Liste, werte es aus, lass den user drauf reagieren, in dem Dialogboxen aufpoppen laesst o.ä.
Passen die daten zu gar nix, ignorier sie oder bring ne fehlermeldung.

Solange du selber deinen prozessor ned in ne lange bearbeitungsschleife schickst, brauchst du eigentlich kein multithreading.

Ciao ...

Verfasst: 8. Oktober 2009 12:55
von Apollinaris
Definier mal, die UI soll nicht einfrieren genauer, was du dir drunter vorstellst.
Damit meiner ich, wenn beide Semaphoren eine P-Operation ( sem->acquire() ) machen. So blockiert der GUI-Thread, weil sie nicht dran kommt. So kann ich nichts machen in meiner GUI. Aber dies war nur eine Notlösung, wie du schon sagst pro Thread/Prozess jeweils nur eine Semaphore.

Und die eine brauche ich um meinen Ablauf zu steuern. Damit der Thread nicht die ganze Zeit läuft und zu 100% meinen Prozessor belastet, sondern nur wenn er aufgefordert wird. Seine Routine durchzuführen.

Wieso ich mich für eine Semaphore entschieden hab und nicht für eine Mutex. Wenn ich die Mutex in der Methode run() meines Kommunikations Threads besetze. Und diese bei einen neuen UDP Paket freigebe, wenn das readyRead Signal kommt. So stürzte mir die Anwenung ab. Weil da wohl bei dem Socket noch ein Thread im Hintergurnd mitläuft. Da hat mir jemand einen Tipp gegeben, ich sollte eine Semaphore benutzen. Weil die nicht vom gleichen Thread freigeben muss. Sondern von den Socket Thread, der wohl im Hintergrund läuft. Mutexe haben einen Besitzer und nur der kann diese wieder freigeben. Ich dies den so richtig?

Jetzt noch mal zu meinen Problem. Ich versuche es etwas besser zu erklären, wie dies von mir so geplannt ist.
Ich möchte in meiner GUI über meinen Kommunikations Thread einen Prozess auf meiner Hardware starten. Mit der ich über UDP kommuniziere. So am Anfang möchte ich prüfen, ob sie überhaupt da ist. Deswegen schicke ich ein Paket und warte auf empfang. Wenn dies geklappt hat soll anschließen ein weiteres Paket verschieckt werden, der irgendwelche Parameter prüft. Wenn diese mit dem Parametern aus der GUI übereinstimmen soll über ein drittes Paket ein Prozess auf der Hardware gestartet werden. Diese drei Aktionen sollen über eine Routine durchgeführt werden. Ohne das der Benutzer da irgendwo eingreift, außer es tretten irgendwelche Fehler auf. Dies möchte ich von der GUI aus betreiben. Aber ich kann nur ein Paket schicken, wenn das zuvor angekomme Paket ausgewertet hab. Ich hoffe jetzt ist das bischen klarer geworden, wofür der Aufwand.

Ganz vereinfacht dargestellt:

Code: Alles auswählen

bool GUI::anlageStarten()
{
  bool rc;

  rc = Kom->schritt1();

  if ( rc == false)
    return false;

  rc = Kom->schritt2();

  if ( rc == false)
    return false;

  rc = Kom->schritt3();

  return rc;
}
Mir gefällt die Idee mit der Liste, aber ich weiß noch nicht, wie man es implementieren könnte. Also, wenn ich das richtig verstehe, soll der Kommunikationsthread über ein Signal einen Eintrag in die Liste machen. Den ich in der GUI auswerte. Aber hab ich nicht das Problem, das ich in der GUI immer aus der Liste lesen muss, bis da ein Eintrag ist. Das wird doch der Prozessor voll ausgelastet sein. Oder gibt es da auch eine Möglichkeit zu warten bis ein Eintrag gemacht wurde, ohne die ganze Zeit die Liste auszulesen?

Verfasst: 8. Oktober 2009 14:08
von RHBaum
Also, wenn ich das richtig verstehe, soll der Kommunikationsthread über ein Signal einen Eintrag in die Liste machen.
Ich wuerd gar keine Threads machen .... warum auch? Dein Socket ist doch schon assynchron, ob das im Hintergrund ueber threads oder nen anderen Proces oder ueber den Kernel geht, ist doch wurscht.

dein nutzer stoesst doch den prozess an, richtig ? Also der Benutzer drueckt nen Button ... dein GUI Thread springt aus der eventschleife in deinen Slot.
Dort schickst ihn ein UDP Packet auf ein vorher vorbereiteten socket. Das schicken ist nicht blockierend, d.h. es kommt sofort zurueck.
DU machst nen eintrag in der Liste, das du so ein packet geschickt hast.
du gehst wieder ausm Slot raus, das heisst dein Thread taucht wieder in die eventloop ab.

Jetzt kommen daten auf dem socket an. DU hasst vorher das readyRead Signal von dem socket mit einem slot von dir connected . Dadurch sendet der socket nen event in deinen gui thread, die eventschleife schickt dann deinen Thread in den connecteden slot .... voiala, du weisst das nen UDP packet angekommen ist da ... liest es aus, schaust ob es das ist was du erwartet hasst. reagierst drauf. jenachdem nimmst den eintrag aus der lsite, schickst nen weiteres packet ab, machst dafuer nen eintrag in der Liste, gehst wieder aus dem slot raus, dein thread verschwindet in der eventloop.
Wenn die antwort kommt, schiesst es wieder deinen Slot der mit dem readyRead signal verbunden ist .....

Die Liste mit dem eintrag brauchst ja nur, um zu wissen in welchen "Schritt" sich deine App befindet, damit das packet auch richtig auswerten kannst .... Also ob du beim 1.request, 2 oder 3 bist ...
Mutexe haben einen Besitzer und nur der kann diese wieder freigeben. Ich dies den so richtig?
Jein. Der begriff Mutex definiert nicht, wie das ding implementiert ist, sondern beschreibt eher die Anforderung, was es gewaehrleisten muss, nämlich eine Critical section -> also einen code-Abschnitt wo sich immer nur ein Thread aufs mal drinn aufhalten soll.
Nen Mutex wird auch gern als lock bezeichnet.
das ein thread einen Mutex "lockt" und ein anderer ihn "entlockt" ist eher ungewoehnliches, eher riskantes verhalten ^^ normal lockt ein thread, und entlockt ihn selber wieder ^^
Nen explizieten besitzer hat nen mutex meist nicht ... sondern irgendwer erzeugt den halt. Wenn du mit besitz meinst, der der den "Lock" grad haelt, ok, dann sollt eigentlich schon aus logischen gruenden, nur der der gelockt hat den lock wieder freigeben koennen. Alles andere waere, naja umstaendlich, fehleranfaellig ... etc.

Lies dir mal bissi literatur zu multithreading durch, ohne detailiertes wissen wirst du da in Teufels Kueche kommen ....

Wobei du bei deinem project, wenn ichs richtig ueberschau, kein MT wirklich bräuchtest ....

Ciao ...

Verfasst: 8. Oktober 2009 15:06
von pfid
RHBaum hat geschrieben: das ein thread einen Mutex "lockt" und ein anderer ihn "entlockt" ist eher ungewoehnliches, eher riskantes verhalten ^^
Macht weder logisch (was schütze ich mit meinem Locken, wenn es jeder andere zu beliebiger zeit unlocken kann?) noch technisch (bei POSIX resultiert das in undefiniertem Verhalten) Sinn.

Verfasst: 8. Oktober 2009 15:08
von Apollinaris
Ja, du hast recht. Der den lock macht muss den unlock machen. Da ich einen Timer nutze, der ggf. reagieren soll, falls kein Paket zurück kommt oder halt den Socket beim readyRead Signal. Und meines Wissens nach sind das zwei Threads die parallel dazu laufen. Können diese die Mutex nicht entsperren. Da es halt andere Threads sind.

Zu deinem Vorschlag mit der Literatur. Kannst du mir etwas empfehlen? Ich kenne nur Threads, Semaphoren und Mutexe. Aber ich denke mein Wissen ist nicht ausreichend, für ein Projekt mit Multithreading. Deswegen würde ich gern mehr in das Thema einarbeiten. Da ich davon ausgehe das bei größeren Projekten man ohne Threads nicht auskommt. Die synchronisation geschieht wohl nur über Mutexe oder halt Semaphoren. Deswegen ist dein Vorschlag schon sehr gut. Hab bisher kein Buch gefunden, nur für System mit UNIX.

Ich habe noch einen dritten Thread neben der GUI und der Kommunikation. Deswegen will ich dieses Konzept beibehalten und nicht nur auf einen Thread umsteigen.

Ich hab mal noch eine doofe Frage. Kann ich nicht aus dem kommunikationsthread der GUI ein Signal schicken, welche den Solt aufruft mit dem nächsten Schritt. Also ich rufe Schritt1() auf. So dann kommt ein Paket. Ich schaue ob alles okay ist. Anschließend bevor ich aus dem Lesen rausgehe, schicke ich der GUI ein Signal, welchen den SLOT schritt2() aufruft. Wenn das nächste Paket angekommen ist. Schaue ich wieder wo bin ich und schicke der GUI ein weiteres Signal, welche den letzten Slot aufruft schritt3(). Ist das eine Möglichkeit, die synchronisation über das Konzept der Signale und Slots zu versuchen?

Verfasst: 9. Oktober 2009 10:10
von RHBaum
Deswegen will ich dieses Konzept beibehalten und nicht nur auf einen Thread umsteigen.
Naja, Threads koennen nicht nur positives, sondern auch negatives bewirken :-) Multithreaded programme muessen nicht schneller sein als singlethreaded. Und einige Projekte haben es uns auch gelehrt, der Synchronisationsoverhaed kann schon gewaltig werden wenn man ned aufpasst. In den meisten faellen resultiert aber MT "nur" in hoeheren Programmieraufwand, die meisten Programme warten sowieso immer auf irgendwas :-)
Wenn man das gleiche ohne MT erreichen kann, ist das "ohne" meist der bessere, performantere weg. In deinem Fall wird die asynchronitaet eh schon durch den Socket intern durch die qt realisiert, deswegen wuerd ich da auf MT verzichten. Threads einfuehren um operationen zu "trennen", zu gliedern bzw. um eine "saubere Zuordnung" zu erreichen iss meist keine gute Idee, weil das Gegenteil erreichst. MT macht vieles um Welten komplexer.
Gibt eigentlich 2 Stichpunkte, wo ueber MT nachdenken solltest, bzw wo MT sinn macht:

- Du hasst langwierige operationen, die du nicht, bzw nur ganz schlecht unterbrechen kannst. Dann um die App ned einfrieren zu lassen ...
- Dein prozessor hat wirklich was zu tun, und wartet ned nur auf Events von aussen. Das was er zu tun hat, laesst sich gut in seperate gleichzeitig durchfuehrbare und wenig untereinander beeinflussende Operationen teilen, und du hasst bzw willst mehrere kerne ausnutzen fuer bessere performance.

Hab ich was vergessen ???
Ich hab mal noch eine doofe Frage. Kann ich nicht aus dem kommunikationsthread der GUI ein Signal schicken, welche den Solt aufruft mit dem nächsten Schritt. Also ich rufe Schritt1() auf.
Ja das geht seit Qt4. Wichtig ist, das der empfaenger in ner eventloop ist, sonst springt er Dein Slot ned an, also wenn der GUI thread der empfaenger ist, gibts kein problem. Andersrum eher, weil die anderen threads meist in keiner eventloop haengen ... Man kann sie aber auch in eine Schicken, dann geht es bidirektional, man muss aber dann die threads bissi anders designen :-)

Literatur ist schwierig, ich hab selber kein richtig gutes Buch zu. Vieles hab ich aus Buechern ueber socket Programmierung, Linux Systemprogrammierung, Nen visual Studio buch aus alten Tagen (Der rest des buchs war grottenschlecht und eh alles voll auf die MFC gemuenzt, aber der kurze Teil zu Multithreading war ganz ok).

Vielleicht koennen da andere helfen ?

Das Basis-Wissen ist eh ned so umfangreich zu, da wuerdest nichtmal nen handbuch voll kriegen, ausser vielleicht mit internas, die nur die Prozessor-Entwickler intressiert. ... schlimmer ist das "multithreaded denken" dazu, was man erstmal lernen muss.
Vorrausgesetzt man kennt sich scho mit der Basis aus, also zu prozessen, speicher etc ...

Ciao ...

Verfasst: 9. Oktober 2009 11:01
von pfid
RHBaum hat geschrieben:
Vielleicht koennen da andere helfen ?
Viel geholfen hat mir "Programming with POSIX Threads" von David R. Butenhof/Addison-Wesley.

Ist allerdings, wie der Name schon andeutet, für pthreads.