Seite 1 von 1

Threads in C++11

Verfasst: Mi Apr 17, 2013 6:13 pm
von GilbertDur
Hallo, ich nehme aktuell an einem Online-Kurs zum Thema Parallele Programmierung teil und habe vor einiger Zeit dort ein Programming Assignment bekommen. Dabei sollte man mit Hilfe von 2 Threads und einem passenden Synchronisierungskonzept ein kleines PingPong Spiel auf der Konsole realisieren. Also immer abwechselnde Ausgaben: Ping! Pong! Ping! Pong! usw.
Nun wurde das ganze bewertet und ich habe ein paar Rückmeldungen bekommen über Fehler, die ich gemacht habe, die ich aber selbst nicht nachvollziehen kann (wie das beim Threading ja leider öfter ist). Hier mal meine Lösung(C++11):

Code: Alles auswählen

#include <iostream>
#include <thread>
#include <condition_variable>
#include <string>

using namespace std;

mutex m;
condition_variable cond;
bool ready;
const string ping = "Ping!";
const string pong = "Pong!";

void Ping(){
	for(int i=1;i<=3;i++)
	{
		lock_guard<mutex> lck(m);
		cout << ping << endl;
		ready=true;
		cond.notify_one();
	}
}

void Pong()
{
	for(int i=1;i<=3;i++)
	{
		unique_lock<mutex> lck(m);
		cout << pong << endl;
		cond.wait(lck, [] {return ready;});
	}
}

int main()
{
    cout << "Ready... Set... Go!" << endl;

	thread t1(Ping);
    thread t2(Pong);

    t1.join();
    t2.join();

    cout << "Done!" << endl;
	cin.get();

    return 0;
}

Dazu die Comments:
You have a deadlock and the threads are not correctly synchronized. Because Ping it can be executed several times before Pong so the messages wouldn't alternate. Occasionally Ping could be executed three times and finished before Pong, it causes a deadlock.
Natürlich wird bei mir immer Ping! Pong! ausgeführt und nicht mehrere Pings oder Pongs nacheinander.
Unfortunately implementation has an error in using synchronization primitives. I set iteration count to 3000 and execute the program on dual-core cpu, and got the such result: ".. Ping! Pong! Ping! Ping! [stop] ". I suppose the reason is Ping thread doesn't check the condition variable (just set it) and also Pong thread just after successfully finished waiting on condition variable release the mutex while moving to the next for() iteration.
Ich hab es sogar auf 300000 gesetzt und natürlich kein Ping! Ping! bekommen.
increase the number of iterations - it prints "Ping!Ping!Ping!Ping!Ping!Pong!Ping!Pong!Ping" - that is erroneous. I consider this solution as irrelevant.
Ok, das gleich als irrelevant einzustufen, ist schon hart :D

Also zu meiner Frage: Kennt sich jemand mit den Synchronisationsprimitiven von C++11 aus und kann mir meinen Fehler sagen? Die trivialen Tutorial-Beispiele, die es dazu im Netz gibt, helfen mir aktuell nicht weiter. Kann es wirklich an der Bedingungsvariable liegen oder habe ich vielleicht die wait()-Methode falsch verstanden/eingesetzt?

Eigentlich dachte ich der Ablauf wäre so:

Der Ping-Thread sperrt über den Lock und führt Ping aus, setzt ready auf true und informiert eventuell wartende Threads. Anschließend wird der lock gelöst. Ein Pong Thread holt sich den lock und wird durch wait dann wieder schlafen gelegt, bis ready erneut true ist. Und ich sehe gerade, ich setze ready nicht auf false. Kann da der Fehler liegen?

Re: Threads in C++11

Verfasst: Mi Apr 17, 2013 6:38 pm
von Glocke
Hi,

erstmal muss ich gestehen: ich habe von conditions keine Ahnung. Daher weiß ich nicht, ob die die Steuerung deiner Prozesse übernehmen oder nicht. Wenn nicht könnte das der Grund sein! Wichtig ist auch, dass du ready am Anfang nicht definiert hast, so dass dein Programm auch mal mit "Pong" startet.

Unabhängig davon hätte ich das ganze folgendermaßen gebaut - auch wenn das kein wirkliches Eingehen auf deine Lösung ist, sorry :( Aber vllt. hilft es dir:

Code: Alles auswählen

#include <iostream>
#include <thread>
#include <mutex>

int repeat = 3;

enum State {
    PING, PONG
} state;

std::mutex mutex;

void ping() {
    for (int i = 0; i < repeat; i++) {
        // wait for PING
        while (true) {
            mutex.lock();
            if (state == PING) {
                break;
            } else {
                mutex.unlock();
            }
        }
        // ping
        std::cout << "Ping! ";
        state = PONG;
        mutex.unlock();
    }
}

void pong() {
    for (int i = 0; i < repeat; i++) {
        // wait for PONG
        while (true) {
            mutex.lock();
            if (state == PONG) {
                break;
            } else {
                mutex.unlock();
            }
        }
        // pong
        std::cout << "Pong! ";
        state = PING;
        mutex.unlock();
    }
}

int main() {
    std::cout << "Ready... Set... Go!" << std::endl;

    state = PING;
    std::thread t1(ping);
    std::thread t2(pong);
    
    t1.join();
    t2.join();

    std::cout << "Done!" << std::endl;
    std::cin.get();

    return 0;
}
Damit ließe sich das ganze auch schnell auf "Ping Pong Pang Pöng Püng Foo Bar Kellerassel Maus" erweitern - Stichwort Zustandsautomat (auch wenn ich davon auch nicht sonderlich viel Ahnung habe).

Ich hoffe, dass ich dir trotz meiner Unwissenheit helfen konnte :D Ich habe bisher "nur" mit einem oder mehreren Mutex-Objekten gearbeitet.. Aber prinzipiell kannst du dir deine "Steuerung" damit bauen wie du sie brauchst

LG Glocke

Re: Threads in C++11

Verfasst: Do Apr 18, 2013 3:35 pm
von GilbertDur
Ich habe es so verstanden, dass man über die condition variables halt Threads wecken und schlafen legen kann :D . Dass das Programm mit "Pong" startet, ist laut Aufgabenstellung egal, daher habe ich das konsequent ignoriert. Ich wollte direkte mutex.lock() und mutex.unlock Aufrufe nicht machen,da im Allgemeinen davon abgeraten wird. Dennoch danke für die Anregungen. :mrgreen:

Re: Threads in C++11

Verfasst: Do Apr 18, 2013 4:00 pm
von Glocke
GilbertDur hat geschrieben:Ich habe es so verstanden, dass man über die condition variables halt Threads wecken und schlafen legen kann :D . Dass das Programm mit "Pong" startet, ist laut Aufgabenstellung egal, daher habe ich das konsequent ignoriert. Ich wollte direkte mutex.lock() und mutex.unlock Aufrufe nicht machen,da im Allgemeinen davon abgeraten wird. Dennoch danke für die Anregungen. :mrgreen:
Das mit dem Wecken und Schlafen-legel kann gut sein. Nur müsste man dazu nicht den exakten Thread irgendwie mit angeben? Ich hab wie gesagt keine Ahnung von conditions - das ist eine reine Spekulation ^^

Re: Threads in C++11

Verfasst: Do Apr 18, 2013 8:53 pm
von GilbertDur
nö, wenn ein thread gerade den lock hat, werden andere threads blockiert. wenn der aktuelle thread schlafen gelegt wird, löst sich der lock und der nächste thread darf ran, kriegt den lock, macht seine sache und weckt den anderen thread sozusagen wieder auf. zumindest ist das mein verständnis von den conditions. ich könnte natürlich falsch liegen. werde da noch mal ein wenig nachforschen müssen :)

Re: Threads in C++11

Verfasst: Di Apr 23, 2013 12:54 am
von jeanluc
Mal in pseudo code:

Code: Alles auswählen


player = A;
Mutex mutex;

function ping() {
 
	// Endlos zocken
	while(True) {
		// A ist dran
		if(player == A) {
		  mutex.lock()
		  print "PING!"
		  player = B
	      mutex.unlock()
		}
	}
}  

function pong() {
 
	// Endlos zocken
	while(True) {
		// B ist dran
		if(player == B) {
		  mutex.lock()
		  print "PONG!"
		  player = A
	      mutex.unlock()
		}
	}
}  



Thread t1(ping);
Thread t2(pong);
    
t1.join();
t2.join();

Allerdings ungetestet. Schon ne Weile her das mutex, semaphore, synchronized Zeug.