-
Hi,
Naja, das ist so ne Sache mit Threads ;).
Mich haben die auch schon mal genervt weil ich mich damit auch nicht so gut auskenne.
Du müsstest die Priorität des Threads runterschrauben. Entweder mit thread->Priority oder mit Mitteln, wie bei meinem Link unten beschrieben.
Das sollte helfen - wenn nicht bin ich auch fast Ratlos...
MfG Alex
-
Ich hoffe das es hier um ein windows kiste geht.
Immer dran denk windows kann kein Multitasking. Sonder es ist TimeSlot
gesteuert. Das heist bei windows 2000 ist es so das alle 18 ms der Trhead gewechselt wird. Jedes program ist mindestens ein Thread wo bei ich keines
kenne was wirklich nur eine hat. Auch wenn den nicht selbst programmiert.
Auch deine Tastatur hängt in dieser Falle. wäre nur anders wenn du die tastatur per DireckX abfragst dann ist die Prioritäte etwas anders.
Zur Instabilität kenn ich ein Fhler den viel am anfang tun. du draf auf keine Fall auch bildschirm element aus dem Thread zugreien oder auf global varialen diese zugriffe muß du sycronnisieren oder anders machen.
Vielleicht hilft die das ja weiter.
Gruß
-
Der Thread im Link von @hl_angel ist offenbar unvollständig.
Pollen ist in Fall einer seriellen Kommunikation schlecht, da hilft auch die Herabsetzung der Priorität nicht viel. Besser ist es, im Thread auf das Empfangen eines(mehrer) Bytes zu warten mit WaitForSingleObjekt(...).
Im folgenden Beispiel wird im Empfangsthread (ComThread) mit WaitForSingleObjekt(...) auf Ereignisse an der seriellen Schnittstelle gewartet. Auf alle muß man natürlich nicht warten, EV_RXCHAR reicht schon. Die Priorität wird hier sogar noch erhöht, um ja auch alles sofort mitzubekommen (hier wird die Zeit gemessen), ohne daß das System damit ausgebremst wird! Und wie die Datenübergabe an den ComThread funktioniert, ist auch in diesem Beispiel zu sehen.
Der Monitorthread ist nicht nowendig, die Ereignisse und Daten vom seriellen Port könne auch anderwohin gesendet werden. ABER: die Verarbeitung der Daten sollte natürlich NICHT im ComThread (oder einer von ihm aufgerufenen Funktion/Klasse) erfolgen, sondern immer gleich (in eine Message-Queue) weggeschrieben und woanders verarbeitet werden.
Code:
/************************************************************
** CommThread : Öffnet, schließt und liest den Port,
** sendet die Zeiten per PostThreadMessage
** an den MonitorThread
************************************************************/
#include <windows.h>
#include <process.h>
#include <stdio.h>
// Prototypes:
DWORD WINAPI ComThread (LPVOID lpParam);
DWORD WINAPI MonitorThread (LPVOID lpParam);
HANDLE StartComThread (DWORD iPortNum, HWND hEdit);
long queryPrecisionTime (void);
long queryPrecisionTimeStamp (void);
// Globals:
HANDLE hCom = INVALID_HANDLE_VALUE;
// global struc contains parameter for threads
typedef struct
{
volatile BOOL ThreadStoppen;
volatile int iComPortNum;
volatile DWORD dwMonitorThreadId;
} PARAMS, *PPARAMS;
static HANDLE hEvent;
// Message types
#define MSG_MIN_RANGE 0
// Der Message-Type für PostThreadMessage setzt sich aus
// QS_ALLPOSTMESSAGE (=256) und dem eigenem Message-Type
// (siehe MSG_xxxxxxx) zusammen.
// Mit MSG_MIN_RANGE können verschiedene Gruppen bei
// Bedarf gebildet werden.
#define MSG_TIME MSG_MIN_RANGE + 1000 // vom ComThread
#define MSG_TRIG MSG_MIN_RANGE + 2000 // reserviert
#define MSG_COMOPEN MSG_TRIG + 4
#define MSG_COMCLOSE MSG_TRIG + 5
#define QEVENT "QueueEvent"
//--------------------------------------------------------------------
// COMThread
//
// erstellt eine Queue, setzt danach den Event ("Queue ist ready").
// Schließt eine geöffneten COM-Port und öffnet den neuen (Nummer ist
// in der Strukur, auf die lpParam zeigt).
// liest die queue aus.
//
// Thread Funktion
// Liest eventgesteuert den Status von CTS, DSR, ... ein und
// sendet in dwEvtMask alle zum Event gehörenden Ereignisse
// zum Monitor-Thread.
//
//--------------------------------------------------------------------
DWORD WINAPI ComThread (LPVOID lpParam)
{
long lTime;
volatile PPARAMS pparams; // struct-Instanz
pparams = (PPARAMS) lpParam; // Zuweisung zu lpParam
int iPortNum = pparams->iComPortNum; // ...
DWORD dwMonitorThreadId = pparams->dwMonitorThreadId; // ...
char szPort[15];
static OVERLAPPED o;
// Maske für SetCommMask, die bestimmt, welche Ereignisse auftreten können
DWORD dwEvtMaskIn = EV_CTS | EV_DSR | EV_BREAK | EV_RING | EV_RXCHAR |
EV_RLSD | EV_ERR | EV_RXFLAG | EV_TXEMPTY;
DWORD dwEvtMask = 0; // Maske, in die WaitCommEvent aktuelle Werte schreibt
BOOL bRet;
CloseHandle (hCom); // "alten" COM-Port schließen
// Com Port öffnen
wsprintf (szPort, "COM%d", iPortNum);
hCom = CreateFile (szPort,
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attributes
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (hCom == INVALID_HANDLE_VALUE)
{
LPVOID lpMsgBuf;
FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf, 0, NULL);
MessageBox (NULL, (LPCTSTR)lpMsgBuf, TEXT("ComThread: error: CreateFile()"),
MB_OK | MB_ICONINFORMATION);
LocalFree (lpMsgBuf);
pparams->ThreadStoppen = TRUE;
}
else
{
if (!SetCommMask (hCom, dwEvtMaskIn))
MessageBox (NULL, "SetCommMask fehlgeschlagen", "COM Port:", MB_OK | MB_ICONSTOP);
// Create an event object for use in WaitCommEvent.
o.hEvent = CreateEvent (NULL, // no security attributes
FALSE, // auto reset event
FALSE, // not signaled
NULL); // no name
}
// Setzt die Priorität "RealTime" für das Programm
DWORD dwProcessId; // own process identifier
HANDLE hProcess; // handle to the own process
DWORD dwDesiredAccess = PROCESS_SET_INFORMATION; // access flag
BOOL bInheritHandle = TRUE; // handle inheritance flag
BOOL bReturn; // If the SetPriorityClass succeeds, then nonzero
DWORD dwPriorityClass = HIGH_PRIORITY_CLASS;
dwProcessId = GetCurrentProcessId ();
hProcess = OpenProcess (dwDesiredAccess, bInheritHandle, dwProcessId);
bReturn = SetPriorityClass (hProcess, dwPriorityClass);
printf ("\r\nComThread: %s geöffnet, warte auf Events ...", szPort);
// eine message zum Monitor-Thread senden:
bRet = PostThreadMessage (dwMonitorThreadId, (QS_ALLPOSTMESSAGE + MSG_COMOPEN),
(WPARAM)iPortNum , (LPARAM)0);
// Auf Events warten:
while (!pparams->ThreadStoppen) // solange weitermachen bis TRUE
{
WaitCommEvent (hCom, &dwEvtMask, &o); // EventMask "scharf machen"
// kommt der Event, ist auch die dwEvtMask geladen und es kann weitergehen
if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, INFINITE)) // warten bis Event
{
lTime = queryPrecisionTimeStamp (); // Zeit holen
// Message senden. In dwEvtMask können mehrere Ereignisse gesetzt sein
bRet = PostThreadMessage (dwMonitorThreadId, (QS_ALLPOSTMESSAGE + MSG_TIME),
(WPARAM)dwEvtMask , (LPARAM)lTime);
} // end of: if (WAIT_OBJECT_0 == ...
} // end of: while (...
CloseHandle (o.hEvent);
CloseHandle (hCom); // COM-Port schließen
printf ("\r\nComThread: %s geschlossen", szPort);
bRet = PostThreadMessage (dwMonitorThreadId, (QS_ALLPOSTMESSAGE + MSG_COMCLOSE),
(WPARAM)iPortNum , (LPARAM)0);
return (0);
}
//----------------------------------------------------------------------------
// MonitorThread
//
// erstellt eine Queue, setzt danach den Event und liest
// die queue aus
//----------------------------------------------------------------------------
DWORD WINAPI MonitorThread (LPVOID lpParam)
{
static char szMsg[255] = "";
static char szMsgTmp[30] = "";
int i = 0;
BOOL bRet;
MSG msg;
volatile PPARAMS pparams; // struct-Instanz
pparams = (PPARAMS) lpParam; // Zuweisung zu lpParam
DWORD COMStatus;
// create a queue for the thread
PeekMessage ((LPMSG)&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// sent event after queue creation back to the threads creator
bRet = SetEvent (hEvent);
printf ("\r\nMonitorThread: warte auf Events ...");
// get messages by message from threads local queue
while ((bRet = GetMessage (&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{ // handle the error and possibly exit
MessageBox (NULL, "MonitorThread: GetMessage error", "COM Port:", MB_OK | MB_ICONSTOP);
return NULL; // error!
}
else
{ // a valid message is received
switch (msg.message)
{
case (QS_ALLPOSTMESSAGE + MSG_COMOPEN):
printf ("\r\nMonitorThread: MSG_COMOPEN: lParam=0x%08x wParam=0x%08x",
msg.lParam, msg.wParam);
break;
case (QS_ALLPOSTMESSAGE + MSG_COMCLOSE):
printf ("\r\nMonitorThread: MSG_COMCLOSE: lParam=0x%08x wParam=0x%08x",
msg.lParam, msg.wParam);
break;
case (QS_ALLPOSTMESSAGE + MSG_TIME):
// Time stamp message: lParam=time, wParam=dwEvtMask
szMsg[0] = '\0';
if (DWORD(msg.wParam) & EV_CTS)
{
GetCommModemStatus (hCom, &COMStatus);
if (COMStatus & MS_CTS_ON)
{ // CTS (Pin8) activated
wsprintf (szMsgTmp, "\r\nCTS_ON : %lu µs", msg.lParam);
}
else
{ // CTS (Pin8) deactivated
wsprintf (szMsgTmp, "\r\nCTS_OFF: %lu µs", msg.lParam);
}
strcat (szMsg, szMsgTmp);
}
if (DWORD(msg.wParam) & EV_DSR)
{
GetCommModemStatus (hCom, &COMStatus);
if (COMStatus & MS_DSR_ON)
{ // DSR (Pin6) activated
wsprintf (szMsgTmp, "\r\nDSR_ON : %lu µs", msg.lParam);
}
else
{ // DSR (Pin6) deactivated
wsprintf (szMsgTmp, "\r\nDSR_OFF: %lu µs", msg.lParam);
}
strcat (szMsg, szMsgTmp);
}
if (DWORD(msg.wParam) & EV_RLSD)
{
GetCommModemStatus (hCom, &COMStatus);
if (COMStatus & MS_RLSD_ON)
{ // DCD (Pin1) activated
wsprintf (szMsgTmp, "\r\nRLSD_ON: %lu µs", msg.lParam);
}
else
{ // DCD (Pin1) deactivated
wsprintf (szMsgTmp, "\r\nRLSD_OFF: %lu µs ", msg.lParam);
}
strcat (szMsg, szMsgTmp);
}
if (DWORD(msg.wParam) & EV_RING)
{
GetCommModemStatus (hCom, &COMStatus);
if ((COMStatus & MS_RING_ON))
{ // RI (Pin9) activated
wsprintf (szMsgTmp, "\r\nRING_ON: %lu µs", msg.lParam);
}
else
{ // RI (Pin9) deactivated
wsprintf (szMsgTmp, "\r\nRING_OFF: %lu µs", msg.lParam);
}
strcat (szMsg, szMsgTmp);
}
printf (szMsg);
break;
default:
wsprintf (szMsg, "\r\nMonitorThread: default: lParam=0x%08x wParam=0x%08x message=0x%08x",
msg.lParam, msg.wParam, msg.message);
MessageBox (NULL, (LPCTSTR)szMsg, "COM Port:", MB_OK | MB_ICONSTOP);
break;
} // end of: switch (msg.message)
} // end of: if (bRet == -1) else ...
} // end of: while((bRet = GetMessage(...
printf ("\r\nMonitorThread: beendet");
return 0;
}
// Globale Variablen
// struct für Zeitmessung
union ut_LargeInteger
{
LARGE_INTEGER o_WinPart;
ULONGLONG l_MyPart;
};
int i_ResetPrecisionTime = 0;
ULONGLONG l_PerfFrequ;
ut_LargeInteger uo_PerfCount;
/************************************************************************
Die Funktion queryPrecisionTimeStamp () liefert die seit dem Start des PC
verstrichene Zeit in Mikrosekunden.
************************************************************************/
long queryPrecisionTimeStamp (void)
{
if (i_ResetPrecisionTime == 0)
{
i_ResetPrecisionTime = 1;
QueryPerformanceFrequency (&uo_PerfCount.o_WinPart);
l_PerfFrequ = uo_PerfCount.l_MyPart;
}
ut_LargeInteger uo_perfCount;
QueryPerformanceCounter (&uo_perfCount.o_WinPart);
return (long) (uo_perfCount.l_MyPart * 1000000 / l_PerfFrequ);
}
// *********************************************************************
int main (void)
{
DWORD dwMonThreadId;
DWORD dwComThreadId;
HANDLE hMonThread;
HANDLE hComThread;
DWORD dwState;
PARAMS p;
// MonitorThread erstellen:
// Erstellt den Event für die Thread Queue
hEvent = CreateEvent (NULL, false, true, QEVENT);
// Thread starten und uMonThreadID für ComThread merken
hMonThread = CreateThread ( // Handle des Threads
NULL, // no security attributes
0, // use default stack size
MonitorThread, // thread function
&p, // argument to thread function
0, // use default creation flags
&dwMonThreadId); // returns the thread identifier
if (hMonThread == NULL)
printf ("\r\nCreateThread (MonitorThread) fehlgeschlagen");
p.dwMonitorThreadId = dwMonThreadId; // merken, für ComThread
// wait for queue completition
dwState = WaitForSingleObject (hEvent, 100);
CloseHandle (hEvent);
// "alten" Thread stoppen
p.ThreadStoppen = TRUE;
CloseHandle (hComThread); // "alten" Thread stoppen
p.iComPortNum = 1;
// ComThread: Starten und Parameterstruct übergeben
p.ThreadStoppen = FALSE;
hComThread = CreateThread ( // Handle des Threads
NULL, // no security attributes
0, // use default stack size
ComThread, // thread function
&p, // argument to thread function
0, // use default creation flags
&dwComThreadId); // returns the thread identifier
if (hComThread == NULL)
printf ("\r\nCreateThread (ComThread) fehlgeschlagen");
Sleep (30000); // nur so zum Test
CloseHandle (hMonThread); // MonitorThread beenden
CloseHandle (hComThread); // ComThread beenden
return (0);
}
Anpassen an die Borland-Sytax/Notation sollte nicht schwer sein.
Blackbird
-
@Blackbrid
Ich hätte mir den link angucken sollen wer pollt der bei der serialen.
man dann verstehe ich das zu den verzögerungen kommt.
Gruß
-
Nicht nur beim Lesen der seriellen Schnittstelle sollte man nicht pollen, sondern auch beim Datei lesen. Geht genauso. Und beim Schreiben ebenfalls mit "overlapped" Funktionen arbeiten, damit nicht der Programmablauf in dieser Zeit blockiert ist. Ist besonders wichtig bei großen Datenmengen (seriell wie auch Datei) und vielen Schreib-Lese-Operationen. Das bringt richtig Speed!
Blackbird
-
Datei lesen tut ich selten schon garnicht im robi.
wenn ich daten holen dann meintesn eine DB.
Und für die logs schreibe ich eh per thread.
Gruß
-
Der Fragesteller hat das Prog ja auch auf einen PC laufen, nicht im Bot. Würde im Bot ein Mainbord mit Windows laufen, gelten die Ausführungen dann aber auch dort.
Dateien müssen nicht unbedingt auf einer HDD, CD oder Floppy liegen ;)
Blackbird
-
bei mir ist das so windows PC im bot *g*
-
OK. Ich hab vielleicht den Anfängerfehler gemacht und auf globale Variabeln, bzw. auf Elemente zugegriffen.
Wie funktioniert denn das mit dem Synchronisieren?
-
Code:
procedureTMyThread.PushTheButton;
begin
Button1.Click;
end;
...
procedure TMyThread.Execute;
begin
....
Synchronize(PushTheButton);
...
end;
ist zwar ein deplhi beispiel aber im Borland C++ gibt es den meines wissens auch.
Denke daber dran das den code auf hält bis sync erreicht.
welche daten will du den vom Thread an das fenster schicken
geht das nicht vielleicht besser mit PostMessage ?
Gruß