Endloseschleife innerhalb eines Threads?



  • Hallo zusammen,

    ich möchte gerne in einem eigenen Thread die serielle Schnittstelle permanent abfragen. Ich hab das bisher jetzt so realisiert, dass, sobald der Benutzer die Verbindung mit der seriellen Schnittstelle herstellen will, ein neuer Thread erstellt wird und innerhalb dieses Threads wird die serielle Schnittstelle in einer Endlosschleife abgefragt. Wenn der Benutzer den entsprechenden Menüpunkt abwählt, wird der Thread mit terminateThread wieder beendet. Jetzt hab ich nur das Problem, dass mein Programm immer langsamer wird, während der Thread läuft, bis es irgendwann überhaupt nicht mehr funktioniert. Liegt das an der Endlosschleife? Wie könnte ich dieses Problem sonst lösen?

    Vielen Dank für Eure Mühe !



  • Das dein Programm natürlich langsamer wird, wenn parallel immer ein Thread läuft, der die ganze Zeit "arbeitet" ist ja normal - so wie ich das jetzt verstanden habe wird dein Programm aber mit der Zeit immer langsamen, oder?! Reservierst du evtl. Speicher in jedem Durchlauf, den du nicht wieder frei gibst?

    Außerdem solltest du den Thread lieber richtig auslaufen lassen, also in deiner Endlos-Schleife immer eine Variable abfragen (die du zum Beenden von außen setzt) und ggf. die Schleife verlassen 🙂



  • 1. Überleg lieber 10 mal bevor du mit TerminateThread nen thread killst. TerminateThread macht nächmlich genua das was der name andeutet, d.h es werden keine resorucen, keine speicher,... mehr freigegeben. Der thread wird gekillt, egal was er grad macht (kann leicht ins auge gehen).
    2. Wie checkst die schnittstellen? Ne endlosschleife mit nem kleinen sleep() die laufen checkt ob was neues da ist? Falls ja, vergiss es... schau dir WaitCommEvent. Mit dem Ding kannst du deinen Thread so anhalten (0 CPU zeit) solange nix da und wenn das passiert worauf die reagieren möchtest startet er los 😉



  • Erstmal vielen Dank für Eure Antworten, das hat mir schon sehr weitergeholfen. WaitCommEvent hört sich sehr gut an, wenn ich das aber richtig verstanden habe, muss ich das auch in einer Schleife einsetzen, damit meine Anwendung permanent auf den Port hört, oder? Der Vorteil hierbei ist aber, dass es weitaus weniger Schleifendurchläufe gibt, da der Thread nicht läuft, wenn kein Signal von der Schnittstelle kommt. Hab ich das alles richtig verstanden?



  • Nein, WaitCommEvent wartet (genauso wie WaitForSingleObject u.ä.) so lange, bis etwas "passiert" (ein Event signalisiert wird). Passiert nix, wacht Dein Thread auch nicht auf!

    Zitat MSDN:
    If hFile was not opened with FILE_FLAG_OVERLAPPED, WaitCommEvent does not return until one of the specified events or an error occurs.

    Also, COM-Schnittstelle NICHT OVERLAPPED öffnen, dann wird Dein Thread so lange blockiert bis etwas passiert und Du drauf reagieren kannst...

    Nix Schleife....



  • Aber so wie ich ihn verstanden habe braucht er weiterhin eine Schleife, da er ja nachdem etwas passiert ist noch weiter auf Daten warten will 😉
    Würde es eigentlich mit ReadFile (also ohne WaitCommEvent) nicht auch direkt gehen? 🙄



  • flenders schrieb:

    Würde es eigentlich mit ReadFile (also ohne WaitCommEvent) nicht auch direkt gehen? 🙄

    kommt drauf an was er machen will 😉



  • mit WaitCommEvent warten sollte kein problem sein, wenn er EV_BREAK mit in der
    Eventmaske hat kann er vom kontrol-thread ein 'SetCommBreak' an den port senden
    und seinen empfangs-thread 'ordentlich' beenden



  • Hallo,
    WaitCommEvent ist ein bisschen tricky, so leicht es sich anfangs auch anhört. Wenn Du das benutzen willst, solltest Du auf jeden Fall die MSDN lesen und diesen Artikel: http://codeproject.com/system/serial_com.asp

    Desweiteren empfehle ich für die Grundlagen:
    http://www.lookrs232.com/com_port_programming
    Beachte besonders das PurgeComm() - das vergisst man leicht und dann geht gar nichts mehr.

    Meiner Meinung nach solltest Du WaitCommEvent nur benutzen, wenn nicht andauernd Daten vom ComPort kommen und du diese eh immer lesen musst. Dann reicht auch das einfache "Polling-Verfahren" - also, permanent lesen vom COM-Port. Das wäre im Prinzip dasselbe, weil WaitCommEvent dann auch die ganze Zeit Events abfeuern würde. Natürlich musst Du dann darauf achten, dass Dein Lese-Thread nicht andauernd mehr Speicher verbraucht. Auch ein kleines Sleep( 0 ) oder Sleep( 1 ) am Ende der Thread-Schleife wirkt Wunder beim Polling-Verfahren.



  • So, erstmal vielen Dank für Eure Hilfe, das hat mir schon sehr weitergeholfen. Ich muss tatsächlich permanent von der seriellen Schnittstelle lesen, weshalb ich nicht WaitCommEvent, sondern nur ReadFile innerhalb einer Schleife verwende. Allerdings empfange ich keine Daten. Obwohl das Gerät sendet, kommt bei mir im Buffer nichts an.
    Ich poste Euch mal den Quellcode, vielleicht könnt Ihr mir helfen:

    //thread function; opens com port and reads data from it
    DWORD WINAPI receiveGPSData(LPVOID lParam)
    {
    	short* rec = (short*)lParam;
    	wchar_t* data = new wchar_t[2048];
    	wchar_t* port_str = new wchar_t[6];
    	wchar_t* msg = new wchar_t[100];
    	DCB dcb;
    	COMMTIMEOUTS cto;
    	DWORD bytesread=0, mask=0;
    	swprintf(port_str,L"COM%d:",port);
    	//establish connection to serial port
    	hComPort = CreateFile (port_str,GENERIC_READ,0,0,OPEN_EXISTING,0,0);
    	if(GetCommState(hComPort,&dcb))
    	{
    		//set com port state
    		dcb.BaudRate = CBR_4800;
    		dcb.Parity = NOPARITY;
    		dcb.StopBits = 1;
    		dcb.ByteSize = 8;
    		if(!SetCommState(hComPort,&dcb))
    			MessageBox(NULL,L"Fehler beim Setzen der Port-Einstellungen",L"Achtung",MB_OK | MB_ICONERROR | MB_APPLMODAL);
    	}
    
    	if(GetCommTimeouts(hComPort,&cto))
    	{
    		//set com port timeouts
    		cto.ReadIntervalTimeout			= 200;
    		cto.ReadTotalTimeoutConstant	= 200;
    		cto.ReadTotalTimeoutMultiplier	= 200;
    		if(!SetCommTimeouts(hComPort,&cto))
    			MessageBox(NULL,L"Fehler beim Setzen der Timeout-Eigenschaften",L"Achtung",MB_OK | MB_ICONERROR | MB_APPLMODAL);
    	}
    
    	if(hComPort != INVALID_HANDLE_VALUE)
    	{
    		while(*rec)
    		{
    			wcscpy(data,L"");
    			ReadFile(hComPort,&data,wcslen(data),&bytesread,NULL);
    			PurgeComm(hComPort,PURGE_TXCLEAR|PURGE_RXCLEAR);
    			if(bytesread>0)
    			{
    				wcscpy(msg,L"");
    				swprintf(msg,L"%s",data);
    				MessageBox(NULL,msg,L"Achtung",MB_OK);				
    			}
    			Sleep(1);
    		}
    		CloseHandle(hComPort);
    	}
    	delete data;
    	delete port_str;
    	return 0;
    }
    

    Das Problem ist nun, dass data immer leer bleibt. In die Schleife "if(bytesread>0)" springt er also nie rein.
    Die Variable rec wird außen von geändert, wenn der Thread beendet werden soll, das funktioniert auch einwandfrei.

    Vielen Dank für Eure Hilfe!



  • wcslen(data) gibt 0 zurück, d.h. du forderst 0 Bytes an.

    Empfehlenswert ist auch, den Rückgabewert von ReadFile zu prüfen, und ggf. GetLastError aufzurufen.

    Nachtrag:
    &data ist auch falsch, data ist schon ein Zeiger.

    Außerdem musst du Arrays mit delete[] freigeben.



  • Daran lags, tatsächlich !!!!! Vielen Dank, das Problem war "&data", wie kann man nur so blöd sein 🙂



  • Hi,
    und nimm das PurgeComm() aus der Read-Schleife raus. Das muss bloß einmal beim ersten Öffnen gemacht werden.


Anmelden zum Antworten