2010-12-29 5 views
0

c/C++で書かれたシリアルCOMポートからデータを読み取るReadPortという関数が含まれているDLLがあります。この関数は_beginthreadexを使用する別のWINAPI関数からの余分なスレッド内で呼び出されます。 COMポートに読み込むデータがあると、ワーカースレッドはデータを返し、正常終了し、呼び出し側スレッドはワーカのスレッドハンドルを閉じ、dllはうまく動作します。COMポートの読み取り - タイムアウトが発生した後でもスレッドは生き残っています

ただし、COMポートでデータが保留されていない状態でReadPortが呼び出された場合、タイムアウトが発生するとWaitForSingleObjectはWAIT_TIMEOUTを返しますが、ワーカースレッドは終了しません。その結果、毎回約1 MBの仮想メモリが増え、物理メモリがKBを増やし、DLLを呼び出すアプリケーションが不安定になります。私もTerminateThread()を使用するtryiedしかし、私は同じ結果を得た。

私は十分な開発経験を持っていますが、私はc/C++に精通していません。投稿する前に多くの研究をしましたが、残念ながら私は自分の問題を解決できませんでした。

この問題を解決するにはどうすればいいですか?しかし、私は本当にこの種の解決策に固執したい。また、私は、各DLLの機能がすべてのCOMポートに対して何回も呼び出される可能性があるため、何らかの余分なイベントを使用するためにグローバル変数を使用することはできないと言いたいと思います。

ワーカースレッド:

unsigned int __stdcall ReadPort(void* readstr){ 

DWORD dwError; int rres;DWORD dwCommModemStatus, dwBytesTransferred; 
int ret; 
char szBuff[64] = ""; 

ReadParams* params = (ReadParams*)readstr; 

ret = SetCommMask(params->param2, EV_RXCHAR | EV_CTS | EV_DSR | EV_RLSD | EV_RING); 
if (ret == 0) 
{ 
    _endthreadex(0); 
    return -1; 
} 
ret = WaitCommEvent(params->param2, &dwCommModemStatus, 0); 
if (ret == 0) 
{ 
    _endthreadex(0); 
    return -2; 
} 
ret = SetCommMask(params->param2, EV_RXCHAR | EV_CTS | EV_DSR | EV_RLSD| EV_RING); 
if (ret == 0) 
{ 
    _endthreadex(0); 
    return -3; 
} 

if (dwCommModemStatus & EV_RXCHAR||dwCommModemStatus & EV_RLSD) 
{ 
    rres = ReadFile(params->param2, szBuff, 64, &dwBytesTransferred,NULL); 
    if (rres == 0) 
    { 
     switch (dwError = GetLastError()) 
     { 
      case ERROR_HANDLE_EOF: 
      _endthreadex(0); 
      return -4; 
     } 

     _endthreadex(0); 
     return -5; 
    } 
    else 
    { 
     strcpy(params->param1,szBuff); 
     _endthreadex(0); 
     return 0; 
    } 
} 
else 
{ 
    _endthreadex(0); 
    return 0; 
} 
_endthreadex(0); 
return 0;} 

呼び出しスレッド:

int WINAPI StartReadThread(HANDLE porthandle, HWND windowhandle){ 

HANDLE hThread; 
unsigned threadID; 
ReadParams readstr; 
DWORD ret, ret2; 

readstr.param2 = porthandle; 

hThread = (HANDLE)_beginthreadex(NULL, 0, ReadPort, &readstr, 0, &threadID); 
ret = WaitForSingleObject(hThread, 500); 

if (ret == WAIT_OBJECT_0) 
{ 
    CloseHandle(hThread); 
    if (readstr.param1 != NULL) 
     // Send message to GUI 
    return 0; 
} 
else if (ret == WAIT_TIMEOUT) 
{ 
    ret2 = CloseHandle(hThread); 
    return -1; 
} 
else 
{ 
    ret2 = CloseHandle(hThread); 
    if (ret2 == 0) 
    return -2; 
}} 

は、事前にありがとう、

SNA

は、私は以下の私のコードの一部を投稿してください。

+0

ちょうど文章的なコメント:コードを読むのは難しいです。この質問とは別にリファクタリングすることをお勧めします。これは、共通の引数値を持つ関数を呼び出したり、共通の変数を同じ共通の値に設定したりするセクションに対しては、関数を呼び出して変数を1回だけ設定することです。 –

+0

ありがとうございます。私はあなたが正しいと思いますが、まずこのコードを動作させようとしました。これがうまくいくなら、多分私はそれが書かれたスタイルをいくつか改良するつもりです。しかし、私はいくつかの人々のために、おそらくこれは迷惑なことを理解することができます。とにかくありがとう。 – Sna

+0

これは構造的な問題です。本当にこのコードを再実行する必要があります。重複したI/Oを調べます。あるいは、この種のコードを借りたり盗んだりしてください。 –

答えて

1

WaitCommEventを使用しないでください。待機中のデータがない場合でもReadFileに電話をかけることができます。

SetCommTimeoutsを使用すると、スレッド間通信でタイムアウトを作成する代わりに、ReadFile自体がタイムアウトになります。

+0

WaitCommEventを無効にし、正しい値をCOMMTIMEOUTS構造体に設定すると正しい結果が得られました。 COMMTIMEOUTSのドキュメントから: "ReadDotalTimeoutConstantとReadTotalTimeoutMultiplierの両方のメンバの値が0であるMAXDWORDの値は、バイトが受信されていなくても、読み込み操作がすでに受信したバイトですぐに戻ることを指定します。 – Sna

0

タイムアウト後の呼び出し元スレッドでは、スレッドハンドルを閉じます。これにより、ハンドルの使用が停止されます。ただし、ワーカースレッドはまだ実行中です。もう一度待つループを使用する必要があります。

+0

ええ、あなたは正しいです。しかし、私の目的は、別のアプリケーションのタイマーでこの関数を使用することです。したがって、スレッドが終了せず、別のスレッドが呼び出された場合、奇妙なことが起こります。 – Sna

+0

WaitCommEventでovelappedパラメータを使用し、限られた時間待機します。 –

+0

それはまだポートを忙しくするので、あまり良い考えではありません。 'WaitCommEvent'はデータを受け取るのに必要ではなく、役に立たない。 –

1

WaitForSingleObjectコールの遅延を5000または10000に変更して、問題の頻度が低下すると思います。

エドウィンの答えも有効です。スレッドハンドルを閉じたため、生成されたスレッドが死ぬことはありません。

タイムアウトするまでにReadPortスレッドが開始されているという保証はありません。 Windowsはスレッドを開始するのに時間がかかります。

は、ここではいくつかの提案です:

  • あなたはbeginthreadexの戻り値をチェックすることはありません。スレッドが開始されたことをどのように知っていますか?

  • ReadPortスレッドの起動をStartReadThreadと同期させるのに適した同期方法を使用します。 ReadPortは、動作準備が整ったときに1に設定される整数フラグと同じくらい簡単です。その後、メインスレッドはその時点で真の待機を開始することができます。それ以外の場合は、2つのスレッド間で起こっていることをデバッガを使って知ることができません。 syncメソッドがReadPortが機能していると表示されるまで、WaitForSingleObjectのコールからStartReadThreadにタイムアウトしないでください。

  • ReadFileでシリアルポートから受信したバイトをコピーするのに、strcpyを使用しないでください。 ReadFileは、読み込んだバイト数を示します。その値とmemcpyを使用してバッファを埋める。

  • ReadFileの読み取りが不定でないようにする方法については、herehereを参照してください。 Windowsで永遠にブロックすることは、他の問題の中でも、あなたが殺すことのできないゾンビプロセスを引き起こす可能性があるため、災害のレシピです。

  • ReadPortスレッドで何が起こったのかについては、StartReadThreadには連絡しません。 は、szBuffに何バイト置かれているのですか? theadsの終了コードを取得するには、GetExitCodeThreadを使用してください。文書番号here。スレッドハンドルを閉じた場合は、GetExitCodeThreadを使用することはできません。

+0

残念ながら、 'WaitCommEvent'の呼び出しは' SetCommTimeouts'を無効にします。 'WaitCommEvent'呼び出しを完全に削除する必要があります。 –

+0

親愛なるBen Voigtが以前に投稿したように、WaitCommEventを削除しなければならず、SetCommTimeoutsを私の機会に適したパラメータで呼び出す必要があります。しかし、あなたの発言のすべてにはポイントがあるようで、私はそれらを覚えておきます。ありがとうございました。 – Sna

関連する問題