Problem mit libboost Threads
-
mase schrieb:
Der Mainthread soll sich ja nicht beenden.
Er wird ja auch nicht beendet. Die Ausführung wird nur unterbrochen bis der andere Thread fertig ist. AFAIK ist das doch der Sinn von join oder nicht? Das heißt du kommst aus der OpenStream()-Methode erst wieder raus wenn Play() beendet wurde und der erzeugte Thread mit der Ausführung fertig ist.
-
Dann muss ich also durch Schleifen usw. dafür sorgen, dass der Mainthread
nicht beendet wird, anstatt join zu verwenden?
-
Du benutzt doch den Thread, weil er nebenher laufen soll?! Jetzt ist die Frage: was soll denn der Thread nicht blockieren? Du mußt doch ne andere Aufgabe haben, weshalb du den Thread laufen lassen willst?
-
Hast ja recht. Momentan bin ich noch am Kern meines Programms. Die ganzen
Verwaltungsoptionen werden jetzt nach und nach implementiert. Es finden
zur Zeit nur ein paar Testaufrufe statt. Natürlich hat der Mainthread nachher
noch anderes zu tun. Sonst bräuchte ich das ganze Multithreading ja nicht.
Ich hab jetzt sleeps in den Mainthread eingebaut.
Ich teste immer zuerst eine neue Funktion durch Aufrufe im Mainthread.
-
Noch was anderes. Ist folgendes möglich?
Ich übergebe der Threadfunktion, die eine Methode einer anderen Klasse ist,
eine Referenz auf den Mutex.
Bevor ich den Thread erstelle, sperre ich den Muxex mit mutex.lock().
Die Threadfunktion beginnt mit mutex.lock() gefolgt von mutex.unlock(), dann
die weiteren Anweisungen. Sie soll also erst einmal warten.
Die Threadfunktion soll jetzt erst die weiteren Anweisungen ausführen, wenn
im Mainthread der Mutex mittels mutex.unlock() freigegeben wird.
Der Sinn ist, es soll der Thread erstellt werden, er soll aber trotzdem
warten, bis noch ein paar set-Funktionen ausgeführt wurden, um ihn zu
initialisieren.
Es soll so auch möglich sein, den Thread an verschiedenen Stellen zu
pausieren.
-
Hab's grad getestet. Funktioniert nicht. Die Threadfunktion läuft munter
weiter, obwohl vor dem Erstellen des Threads der Mutex gesperrt wurde.
Der Mutex wird im Mainthread erstellt und sofort gesperrt. Dem neu erstellten
Thread übergebe ich den Mutex als Referenz. Der Thread beginnt mit einem
lock(), gefolg von einem unlock(). Er sollte doch erst weiterlaufen, wenn
der Mutex im Mainthread freigegeben wird. Ich will eigentlich nur den Thread
schlafenlegen und wieder aufwecken können.
Was kann das sein?
-
mase schrieb:
Noch was anderes. Ist folgendes möglich?
Ich übergebe der Threadfunktion, die eine Methode einer anderen Klasse ist,
eine Referenz auf den Mutex.Ein Mutex steht immer in Beziehung zu einem Datum. Folglich sollte das Mutex ein Member derselben Klasse sein, die auch das zu schützende Datum enthält. Eine Übergabe des Mutex ist unüblich.
mase schrieb:
Bevor ich den Thread erstelle, sperre ich den Muxex mit mutex.lock().
Die Threadfunktion beginnt mit mutex.lock() gefolgt von mutex.unlock(), dann
die weiteren Anweisungen. Sie soll also erst einmal warten.
Die Threadfunktion soll jetzt erst die weiteren Anweisungen ausführen, wenn
im Mainthread der Mutex mittels mutex.unlock() freigegeben wird.
Der Sinn ist, es soll der Thread erstellt werden, er soll aber trotzdem
warten, bis noch ein paar set-Funktionen ausgeführt wurden, um ihn zu
initialisieren.
Es soll so auch möglich sein, den Thread an verschiedenen Stellen zu
pausieren... dafür ein Mutex mit gegenseitigem Lock zu nutzen ist ebenso ungewöhnlich.
Dafür nimmt man i.A. die Condition. Bei Dir vielleicht in Kombination mit einem einfachen Flag (bool), welches angibt, ob der Thread (Play) angehalten wird oder nicht (Member m_pause - s.Code unten).
Zunächst einmal würde ich den Thread zum Member der Klasse machen, auf deren Daten der Thread zugreift. Ich unterstelle mal, dass im wesentlichen Informationen des 'cStream' benötigt werden. Also sollte der Thread Member von dieser Klasse sein; entweder direkt oder als Pointer.
Das Play und Pause(mit der Möglichkeit zum Fortfahren) kann dann über eine einfache bool-Variable in Kombination mit der condition erreicht werden. Als Code-Skizze sähe das so aus:
class cStream { public: typedef boost::mutex::scoped_lock lock_type; cStream() : m_running( true ) , m_pause( true ) , m_condition() , m_mtx() , m_thrd( boost::bind( &cStream::Run, this ) ) // Bem.: warning wg. Zugriff auf 'this' ist ok, solange m_thrd letztes Element in cStream ist! {} ~cStream() { m_running = false; // beendet Schleife in Run-Methode Play(); // sicherstellen, dass der Thread nicht in der Pause fest hängt m_thrd.join(); // warte, bis Thread zu Ende ist } void Play() { lock_type lock( m_mtx ); m_pause = false; m_condition.notify_one(); // weckt ggf. die wait-Methode } void Pause() { lock_type lock( m_mtx ); m_pause = true; } private: void Run() // hier rennt der Thread; Methoden-Ende ist auch Thread-Ende { for( ; m_running; ) { check4pause(); // blockiert wenn 'm_pause == true' (s.u.) // .. Stream abspielen } } void check4pause() { lock_type lock( m_mtx ); while( m_pause ) { m_condition.wait( lock ); // blockiert bis 'notify' } } // -- Member bool m_running; bool m_pause; boost::condition_variable m_condition; boost::mutex m_mtx; boost::thread m_thrd; };Gruß
Werner
-
Die Sache ist so:
Die Klasse cStream bekommt vom Mainthread einen Pointer auf ein Objekt
von cPlaylist. Den Mutex erzeuge ich im Mainthread, und übergebe ihn
an cPlaylist und cStream. cStream::Play() läuft nun als Thread in einer
Schleife. Dabei greift er sich jedesmal den zu spielenden Titel von
cPlaylist ab. Im cPlaylist-Objekt kann sich aber während der Laufzeit
die Playliste ändern. Um ein gleichzeitiges Lesen und Schreiben zu verhindern,
wollte ich dazu den gleichen Mutex verwenden.
Ich könnte mir zum Pausieren einen weiteren Mutex als Member von cStream
anlegen, wie du gesagt hast.
-
.. Du kannst auch bei dem von mir beschriebenen Vorschlag den gleichen Mutex nutzen.
Wie schon gesagt besteht zwischen einem Mutex und den zu schützenden Daten eine 1:1-Beziehung. In meiner Skizze wird nur 'bool m_pause' vom Mutex geschützt; es wäre aber kein Problem eine Playlist mit aufzunehmen.
Man muss nur beachten, dass der Mutex innerhalb des cStream nicht zu lange gelockt bleibt. Das erreicht man z.B. in dem man in der Run-Methode unter Schutz des Mutex (gelockt) eine lokale Kopie eines Teils der Playlist anlegt und anschließend diesen ohne lock abspielt.Gruß
Werner
-
Du meinst, dass der Stream in der Lockzeit nicht abreisst?
Ich habe mir überlegt, das über einen vector von buffern zu machen (FIFO).
Aber ist das in meinem Fall ok, den Mutex im Mainthread zu erzeugen und
an beide Klassen zu übergeben?
Mit dem separaten Mutex erreiche ich aber auch, dass ich während der Pause-
phase die Playlist modifizieren kann. Das wäre mit einem gemeinsamen wohl
nicht möglich.
Aber deine Methode funktioniert tatsächlich.
In meiner Boost-Doku steht gar nix von mutex::scoped_lock und notify_one().
Danke dir!
-
mase schrieb:
Du meinst, dass der Stream in der Lockzeit nicht abreisst?
ich dachte eher daran, dass man von außen Pause() aufruft und der cStream mehr oder weniger unmittelbar reagiert, ohne erst das ganze aktuelle Lied abzuspielen - aus was für Einheiten besteht denn so eine 'PlayList' und wie lange dauert das Abspielen so einer Einheit?
mase schrieb:
Ich habe mir überlegt, das über einen vector von buffern zu machen (FIFO).
Aber ist das in meinem Fall ok, den Mutex im Mainthread zu erzeugen und
an beide Klassen zu übergeben?Kann ich so pauschal nicht sagen. Ein Mutex ist nach meinem Verständnis dafür nicht gemacht ...
mase schrieb:
Mit dem separaten Mutex erreiche ich aber auch, dass ich während der Pause-
phase die Playlist modifizieren kann. Das wäre mit einem gemeinsamen wohl
nicht möglich.Sicher - warum sollte das nicht möglich sein. Lasse Dich von dem Lock in 'check4pause' nicht beirren. Innerhalb von condition::wait wird der lock wieder geöffnet, sonst würde der Play ja auch nicht funktionieren.
mase schrieb:
Aber deine Methode funktioniert tatsächlich.
In meiner Boost-Doku steht gar nix von mutex::scoped_lock und notify_one().guckst Du hier: mutex::scoped_lock & notify_one()
Gruß
Werner
-
Pause regiert direkt. Der Pausencheck wird bei jeden Füllen des Buffers
ausgeführt. Und das sind 4kB Blöcke.
-
Jetzt habe ich ein neues Problem:
Ich hab die Erzeugung des neuen Threads aus der Mainfunktion rausgenommen und
in die Klasse cStream eingebaut.
Das sieht jetzt so aus:Hauptprogramm
int main() { cStream *Stream = new cStream(true); Stream->OpenStream(); Stream->StartStream(); boost::this_thread::sleep(boost::posix_time::seconds(5)); Stream->PauseStream(true); delete Stream; }Klasse cStream
void cStream::PauseStream(bool _Pause) { if (mPause == _Pause) { return; } if (!_Pause) { boost::mutex::scoped_lock Pause(mPauseMutex); mPause = false; mPauseCondition.notify_one(); return; } boost::mutex::scoped_lock Pause(mPauseMutex); mPause = true; return; } int cStream::OpenStream() { //Hier wird die Serververbindung hergestellt } void cStream::StartStream() { boost::thread StreamThread(boost::bind(&cStream::Send, this)); } void cStream::CheckPause() { boost::mutex::scoped_lock Pause(mPauseMutex); while (mPause) { mPauseCondition.wait(Pause); } } void cStream::Send() { while (!mStop) { //pause stream? CheckPause(); //Jetzt werden weitere Funktionen zum Bufferfüllen und Abspielen //aufgerufen } } void cStream::StopStream() { mStop = true; PauseStream(false); return; }Alle Variablen, die mit m beginnen sind als Member in der Headerdatei
deklariert.
In der Funktion cStream::StopStream() musste ich zuerst die Pause aufheben,
sonst stürzt das Programm an dieser Stelle ab.
Mein Problem ist jetzt, dass wenn länger als ca. 10 Sekunden pausiert und dann
die Pause aufgehoben wurde, sich das Programm ohne Fehler beendet.
Es existiert auch kein Servertimeout.
Ich kann mir nicht erklären, warum das so ist.
-
Ich hab das ganze durch den Debugger laufen lassen. Nach dem Aufheben der
Pause wird der Buffer gefüllt und über libshout an den Server gesendet.
Dann wird der Buffer noch einmal gefüllt. Wenn jetzt die Sendefunktion
über libshout aufgerufen wird, erhalte ich folgende Meldung:Das Programm hat das Signal SIGPIPE (Broken pipe) empfangen.
Das ganze immer beim zweiten Sendeblock. Und von libshout kommt keine
Fehlermeldung zurück. Pausiere ich nur 5 Sekunden, läuft alles problemlos.
-
Hab die Ursache gefunden. Es ist tatsächlich ein Servertimeout, welcher sich
aber wahrscheinlich nicht abfragen lässt. Ich baue jetzt nach dem Ende der
Pause die Verbindung neu auf. Funktioniert ganz gut.