Klassenfunktion mit Parameter als Thread
-
Vielen Dank.
Die Antworten haben mir sehr geholfen.
-
Gerne möchte ich die Funktion als Thread ausführen und ihm neue Nachrichten zukommen lassen. Dafür habe ich 2 Funktionen. Leider wird zwar der Logger (zum Speichern angedacht) ausgeführt. Doch es werden keine neuen Nachrichten ausgegeben (/gespeichert).
Kann es an dem lock eventuell liegen?
void clogging::start() { std::cout << "logging start" << std::endl; while (1!=0) { if (mutex.try_lock()) { if(vsmessage.size()==0) { } else if(vsmessage.size()>0) { std::cout << vsmessage << std::endl; vsmessage.pop_back(); } else { } mutex.unlock(); } else { mutex.unlock(); } // for cpu and add new message mutex.unlock(); std::this_thread::sleep_for(500ms); } } void clogging::addmessage(std::string stext) { std::cout << "addmessage" << std::endl; while (true) { if (mutex.try_lock()) { std::cout << "job shared (" << " message " << ")\n"; vsmessage.push_back(stext); mutex.unlock(); return; } else { vsmessage.push_back(stext); std::cout << "job exclusive (" << " message " << ")\n"; } mutex.unlock(); std::this_thread::sleep_for(500ms); } }clogging logging; std::thread tlogging(&clogging::startA, &logging); tlogging.join(); std::string msg = "Hallo Welt"; std::thread t1(&clogging::addmessageA, &logging, msg); t1.join();Vielen Dank im Voraus!
-
@no_name1991 Kannst du den Code so zusammen stellen, dass wir direkt ein kompilierbares Beispiel haben? Dann müssen wir nicht annehmen, wo der Mutex lebt, in welchem Kontext deine Threads erstellt werden und sehen schneller, wo die Probleme liegen können.
-
ich entschuldige mich dafür und reiche hier meinen Code (compilierbar) nach:
#include <algorithm> #include <filesystem> #include <fstream> #include <iostream> #include <string> #include <mutex> #include <thread> using namespace std::chrono_literals; class clogging{ private: std::vector<std::string> vsmessage; std::mutex mutex; protected: public: void start() { std::cout << "logging start" << std::endl; while (1!=0) { if (mutex.try_lock()) { if(vsmessage.size()==0) { } else if(vsmessage.size()>0) { std::cout << vsmessage[0] << std::endl; vsmessage.pop_back(); } else { } mutex.unlock(); } else { } std::this_thread::sleep_for(500ms); } } void addmessage(std::string stext) { std::cout << "addmessage" << std::endl; while (true) { if (mutex.try_lock()) { vsmessage.push_back(stext); mutex.unlock(); return; } else { } std::this_thread::sleep_for(500ms); } }; }; int main() { clogging logging; std::thread tlogging(&clogging::start, &logging); tlogging.join(); std::string msg = "Hallo Welt"; std::thread t1(&clogging::addmessage, &logging, msg); t1.join(); return 0; }
-
@no_name1991
Da fehlt noch der Vektor include
https://en.cppreference.com/w/cpp/thread/thread/join.html
joinwartet, dass der Thread beendet wird. Da du da eine unendlich Schleife drinnen hast, wird das aber nie passieren.Edit:
int main() { clogging logging; std::thread tlogging(&clogging::start, &logging); std::string msg = "Hallo Welt"; std::thread t1(&clogging::addmessage, &logging, msg); tlogging.join(); t1.join(); return 0; }so siehst du zumindest eine Ausgabe

-
@no_name1991
Mal eine Frage: Warum rufst duaddmessagein einem eigenen Thread auf und nicht im Hauptthread?
-
#include <algorithm> #include <filesystem> #include <fstream> #include <iostream> #include <string> #include <mutex> #include <thread> using namespace std::chrono_literals; class clogging{ private: std::vector<std::string> vsmessage; std::mutex mutex; protected: public: void start() { std::cout << "logging start" << std::endl; while (1!=0) { if (mutex.try_lock()) { if(vsmessage.size()==0) { } else if(vsmessage.size()>0) { std::cout << vsmessage[0] << std::endl; vsmessage.pop_back(); } else { } mutex.unlock(); } else { } std::this_thread::sleep_for(500ms); } } void addmessage(std::string stext) { std::cout << "addmessage" << std::endl; while (true) { if (mutex.try_lock()) { vsmessage.push_back(stext); mutex.unlock(); return; } else { } std::this_thread::sleep_for(500ms); } }; }; int main() { clogging logging; std::thread tlogging(&clogging::start, &logging); tlogging.join(); std::string msg = "Hallo Welt"; //std::thread t1(&clogging::addmessage, &logging, msg); //t1.join(); logging.ddmessage(msg); return 0; }Dieser Code gibt nur die als Output "logging start" aus.
Mein Ziel ist es, Ein/-Ausgaben, Zustände etc auszugehen bzw diese eventuell auf Festplatte zu speichern.
Dies soll dann das restliche Programm nicht blockieren (warten bis die HDD etc es beendet hat).
-
@no_name1991 Du wartest darauf das
tloggingbeendet wird, bevor duaddmessageaufrufst,startkommt aber nie zurück, daher wirdaddmessageauch schon nicht aufgerufen.
-
@Schlangenmensch
Vielen Dank für die Erklärung.Wie muss man den Code verändern, wenn man vsmessage etwas hinzufügen möchte und der Thread parallel abgearbeitet werden soll?
Vielen Dank im Voraus.
-
Hallo @no_name1991 ,
in einem Projekt habe ich mich mal mit einem Threads beschäftigt, der über eine Message-Queue Nachrichten bekam, die dieser abgearbeitet hat. War die Queue abgearbeitet, würde der Threads "schlafen gelegt". Würde etwas in die Queue abgelegt, würde der Threads "aufgeweckt". Das Einfügen und Entnehmen aus der Queue würde mit einem Mutex abgesichert. Wärend der Threadarbeit könnten andere Threads etwas in die Queue einfügen. Das ganze ist mMn nur sinnvoll, wenn der Threads auch gut zu tun hat.
Du solltest prüfen, ob eine einfache Ausgabe im Ablauf ohne zusätzlichen Thread nicht sinnvoller wäre.
-
@no_name1991 Du könntest das
tlogging.join();ans Ende der main() setzen, ähnlich dem, was ich dir oben schon im Postskriptum vorgeschlagen habe.Prinzipiell solltest du dir
std::jthreadanschauen. Das ist eine bessere Variante, die bei Zerstörung des Thread Objekts automatisch Joint.Außerdem solltest du dir
std::lock_guardanschauen, und damit die Locks realisieren, damit kannst du kein unlock vergessen.Wenn dein eigentliches Anliegen ist, den Logger Asynchron zu haben, könnte das auch ein Anwendungsfall für eine Coroutine sein, die sind in C++ aber noch ziemliches Neuland.
-
@no_name1991 sagte in Klassenfunktion mit Parameter als Thread:
Mein Ziel ist es, Ein/-Ausgaben, Zustände etc auszugehen bzw diese eventuell auf Festplatte zu speichern.
Dies soll dann das restliche Programm nicht blockierenDas ist aus meiner Sicht ein valides Szenario (oder Use-Case bzw. User-Story), und es riecht nach dem klassischen Producer-Cosumer-Prinzip, wie es auch schon Jakoby beschrieb.
Möglicherweise genügt es aber auch, wenn es nur einen Cosumer gibt - also keine Warteschlange. Dann läufst du aber in die Gefahr, dass bei sehr vielen Logging-Aufrufen und langsamem Speichermedium eben doch blockiert wird.
Beim klassischen Producer-Consumer (in Intervallen) muss sichergestellt werden, dass das Put und Take nicht gleichzeitig passieren darf (MutEx bzw. Synchronisation). Es gibt auch die Möglichkeit, dass der Consumer wartet und vom Producer benachrichtigt wird ... oder sich beide gegenseitig benachrichtigen, dann entsteht aber immer die Dead-Lock-Möglichkeit, die ausgeschlossen werden muss. Für den Anfang würde ich in Intervallen checken, ob es etwas zu loggen gibt, und dann wieder je eine feste oder dynamische Zeit schlafen.
-
@Schlangenmensch sagte in Klassenfunktion mit Parameter als Thread:
Außerdem solltest du dir std::lock_guard anschauen, und damit die Locks realisieren, damit kannst du kein unlock vergessen.
Clang-tidy hat mich vor kurzem auf std::scoped_lock hingewiesen:
https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-scoped-lock.html
https://en.cppreference.com/w/cpp/thread/scoped_lock.html
Der Vorteil des Ganzen:
If several mutexes are given, deadlock avoidance algorithm is used as if by std::lock.