Problem mit critical Section



  • Hallo, ich habe 2 Threads. Ein Produzenten und einen Konsumenten.

    Der Produzent soll die produzierten Objekte in eine Queue legen und der Konzument soll sich diese abholen.

    Nun habe ich versucht eine Thread sichere queue zu schreiben:

    #include <queue>
    
    using namespace std; 
    
    #ifndef Queue_Thread_sicher__ 
    #define Queue_Thread_sicher__ 
    
    template<class T> class Queue_Thread_sicher
    
    {
    private:
    	CRITICAL_SECTION CriSect;
    	queue<T> *q;
    
    public:
    
    	//Konstruktor
    	Queue_Thread_sicher() 
    	{
    		q= new queue<T>();
    		InitializeCriticalSection(&CriSect);
    	}
    
    	//Destruktor
    	~Queue_Thread_sicher() 
    	{
    		DeleteCriticalSection(&CriSect);
    		delete q;
    	}
    
    	//liefern den Wert des letzten Elements .
    	T back () 
    		{	 
    		EnterCriticalSection(&CriSect);
    			return q->back();
    		LeaveCriticalSection(&CriSect) ;	
    	}
    
    	//gibt zurück, ob die queue leer ist
    	bool empty()
    	{
    		EnterCriticalSection(&CriSect);
    			return q->empty();
    		LeaveCriticalSection(&CriSect);	
    	}
    
    	//liefern den Wert des ersten Elements .
    	T front () 
    	{
    		EnterCriticalSection(&CriSect);
    			return q->front();
    		LeaveCriticalSection(&CriSect) ;	
    	}
    
    	//löscht das letzte element
    	void pop () 
    	{	
    		EnterCriticalSection(&CriSect);
    			q->pop();
    		LeaveCriticalSection(&CriSect) ;	
    	}
    
    	//Fügt ein Element hinzu
    	void push (T t) 
    	{
    		EnterCriticalSection(&CriSect);
    			q->push(t);
    		LeaveCriticalSection(&CriSect) ;	
    	}
    
    	//gibt die Größe an
    	float zize () 
    	{
    		EnterCriticalSection(&CriSect);
    			return q->size();
    		LeaveCriticalSection(&CriSect) ;	
    	}
    };
    
    #endif
    

    Das Problem ist aber, dass der Produzent ein Objekt produzieren und in die Queue legen kann. Der Konsument nimmt sich diese raus und "verbraucht" es.
    Dann kann der Produzent aber keine weiteren Objekte in die Queue legen, denn er bekommt (anscheinend) keinen Zugriff auf die critical Section.

    Immer wenn ich dann das Programm unterbreche hängt er in der schleife, bei der er (Der produzent) auf den Zugang zur critical Section wartet.

    Was ist Falsch?



  • Hier noch der Code zum Testen:

    #include <iostream>
    #include <winsock2.h>
    #include "Queue_Thread_sicher.h"
    using namespace std; 
    
    DWORD WINAPI Produzent(LPVOID data)
    {
    	//data in Queue_Thread_sicher umwandeln
    	Queue_Thread_sicher<int*> *q = (Queue_Thread_sicher<int*>*) data; 
    
    	while (true) {			//produziere für immer
    
    		int *x = new int (700);	//erzeuge integer zahl
    		q->push(x);			//Füge diese in die Queue_Thread ein
    		cout << *x<<" produziert"<< endl;
    
    	}
    	return (DWORD)data;
    }
    
    DWORD WINAPI Konsument(LPVOID data)
    {
    	//data in Queue_Thread_sicher umwandeln
    	Queue_Thread_sicher<int*> *q = (Queue_Thread_sicher<int*>*) data;
    
    	while (true) {			//konsumiere für immer
    		while (!q->empty())     //wenn die queue nicht leer ist
    		{
    
    				int *x=q->back();  //hole Element aus der Queue
    
    			cout << *x<<" konsumiert"<< endl;
    			delete x;			//lösche das Objekt
    			q->pop();			//Reduziere die Queue um 1
    
    		}
    	}
    	return (DWORD)data;
    }
    
    int main () {
    	//die Threadsichere Queue erzeugen
    	Queue_Thread_sicher<int*> *q = new Queue_Thread_sicher<int*>();
    	HANDLE hThread[2];	//Handle für die Threads
    	DWORD  dwThreadID[2];	//DWORD für die Threads
    
    //Beide Threads starten
    	hThread[0] = CreateThread(NULL,                        
    		0,                            
    		Produzent,           
    		(LPVOID)q,           
    		0,                            
    		&dwThreadID[0]);
    
    	hThread[1] = CreateThread(NULL,                        
    		0,                            
    		Konsument,           
    		(LPVOID)q,           
    		0,                            
    		&dwThreadID[1]);
    
    	//Auf die Threads warten
    	WaitForMultipleObjects(	2,	
    		hThread,			
    		TRUE,				
    		INFINITE);		
    
    	return 1;
    }
    

    Als Ausgabe erscheint nur:

    700produziert 700konsumiert

    und dann nichts mehr.

    Wie musß ich die Klasse "Queue_Thread_sicher" ändern, damit es funktioniert?
    (Oder ist woanders ein Fehler?)



  • MisterX schrieb:

    float zize () 
    	{
    		EnterCriticalSection(&CriSect);
    			return q->size();
    		LeaveCriticalSection(&CriSect) ;	
    	}
    

    Ist dir klar, was hier nicht passiert?



  • MFK schrieb:

    MisterX schrieb:

    float zize () 
    	{
    		EnterCriticalSection(&CriSect);
    			return q->size();
    		LeaveCriticalSection(&CriSect) ;	
    	}
    

    Ist dir klar, was hier nicht passiert?

    Leider nein!
    Bitte schreib was nicht passiert!



  • LeaveCriticalSection passiert nicht. Nach der return-Anweisung ist die Methode beendet.

    Edit: Gerade erst gesehen: Warum in aller Welt float?



  • LoooooL <Brett vorm Kopf zur seite schieb> <schäm>

    Ich frage mich dann nur, warum ich ein Element produzieren und eins kosumieren konnte, wenn ich nie die critical Section freigebe.
    Es hätte nach dem Produzieren doch der Konsument keinen Eintritt mehr bekommen dürfen.



  • Warum float?

    Habe ich mich auch gewundert, aber bei den ganzzahligen typen int, long,
    unsigned int, unsigned long, schreibt er immer als Warnung, dass Datenverlußte
    auftreten könnten, bei float schreibt der Compiler die warnung nicht.



  • MisterX schrieb:

    Es hätte nach dem Produzieren doch der Konsument keinen Eintritt mehr bekommen dürfen.

    Nö. In push hast du das Problem ja nicht.

    Dein Code ist übrigens ein gutes Beispiel für nicht exception-sicheren Code. Warum die Klasse einen Zeiger für die Queue verwendet, habe ich auch nicht verstanden.



  • So, DANKE nochmals
    (Manchmal sieht man die Bäume vor lauter Wald nicht)

    Hier die jetzt funktionierende Version (leider noch ohne Exception Handling)

    #include <queue>
    using namespace std; 
    
    #ifndef Queue_Thread_sicher__ 
    #define Queue_Thread_sicher__ 
    
    template<class T> class Queue_Thread_sicher
    
    {
    private:
    	CRITICAL_SECTION CriSect;
    	queue<T> q;
    
    public:
    
    	//Konstruktor
    	Queue_Thread_sicher() 
    	{
    
    		InitializeCriticalSection(&CriSect);
    	}
    
    	//Destruktor
    	~Queue_Thread_sicher() 
    	{
    		DeleteCriticalSection(&CriSect);
    
    	}
    
    	//liefern den Wert des letzten Elements .
    	T back () 
    		{	 
    		EnterCriticalSection(&CriSect);
    		T help=q.back();
    		LeaveCriticalSection(&CriSect) ;
    		return help;
    	}
    
    	//gibt zurück, ob die queue leer ist
    	bool empty()
    	{
    		EnterCriticalSection(&CriSect);
    		bool help= q.empty();
    		LeaveCriticalSection(&CriSect);
    		return help;
    	}
    
    	//liefern den Wert des ersten Elements .
    	T front () 
    	{
    		EnterCriticalSection(&CriSect);
    		T help=q.front();
    		LeaveCriticalSection(&CriSect) ;
    		return help;
    	}
    
    	//löscht das letzte element
    	void pop () 
    	{	
    		EnterCriticalSection(&CriSect);
    			q.pop();
    		LeaveCriticalSection(&CriSect) ;	
    	}
    
    	//Fügt ein Element hinzu
    	void push (T t) 
    	{
    		EnterCriticalSection(&CriSect);
    			q.push(t);
    		LeaveCriticalSection(&CriSect) ;	
    	}
    
    	//gibt die Größe an
    	float zize () 
    	{
    		EnterCriticalSection(&CriSect);
    		float f= q.size();
    		LeaveCriticalSection(&CriSect) ;	
    		return f;
    	}
    };
    
    #endif
    


  • Oh man bin ich dumm. 😃 Ich werde mich nochmal mit den Grundlagen beschäftigen bevor ich mit Winapi weitermache.



  • Obwohl du unter falschem Namen postest, ist die Idee gar nicht mal so schlecht 😃


Anmelden zum Antworten