1

Windowsの機能を使ったシリアル通信ソフトを開発しています。この CSerialCommhelper はすべてのシリアル通信機能を処理するクラスであり、CphysicalLayer はそのクラスを利用するクラスです。

class CSerialCommHelper :
public CCommAgent
 {
    HANDLE m_pPortHandle;           //Handle to the COM port
    HANDLE m_hReadThread;           //Handle to the Read thread
    HANDLE m_hPortMutex;            //Handle to Port Mutex
    std::wstring m_strPortName;     //Portname
    COMMTIMEOUTS m_CommTimeouts;    //Communication Timeout Structure
    _DCB dcb;                       //Device Control Block
    DWORD m_dwThreadID;     //Thread ID

public:
    CSerialCommHelper(CPhysicalLayer *);
    virtual HRESULT Open();
    virtual HRESULT ConfigPort();
    static void * ReadThread(void *);
    virtual HRESULT Write(const unsigned char *,DWORD);
    virtual HRESULT Close();
    //virtual HRESULT Flush(DWORD dwFlag = PURGE_TXCLEAR | PURGE_RXCLEAR);
    wstring StringToWstring(const string &);
    ~CSerialCommHelper(void);
};

CommAgent には、データを受信したときに physicalLayer に通知するための CphysicalLayer ポインターが含まれています。

HRESULT CSerialCommHelper::Write(const unsigned char *pucDataToWrite,DWORD ulLength)
{
    unsigned long  bytesWritten=0, ij = 0;
    WaitForSingleObject(m_hPortMutex,INFINITE);
    if(WriteFile(m_pPortHandle,pucDataToWrite,ulLength,&bytesWritten,NULL))
    {
    if(!ReleaseMutex(m_hPortMutex))
    {
        DWORD err=GetLastError();

        // Mutex released succesfully..
    }
    }
    if (bytesWritten != ulLength)
            return E_FAIL;
    return S_OK;

}
void * CSerialCommHelper::ReadThread(void * pObj)
{
    CSerialCommHelper *pCSerialCommHelper =(CSerialCommHelper *)pObj; 
    DWORD dwBytesTransferred = 0;
    unsigned char byte = 0;

    while (pCSerialCommHelper->m_pPortHandle != INVALID_HANDLE_VALUE)
    {
        pCSerialCommHelper->m_strBuffer.clear();
        pCSerialCommHelper->m_usBufSize=0;
        WaitForSingleObject(pCSerialCommHelper->m_hPortMutex,INFINITE);
        do
        {
            dwBytesTransferred = 0;
            ReadFile (pCSerialCommHelper->m_pPortHandle, &byte, 1, &dwBytesTransferred, 0);
            if (dwBytesTransferred == 1)
            {
                pCSerialCommHelper->m_strBuffer.push_back(byte);
                pCSerialCommHelper->m_usBufSize++;
                continue;

            }
        }
        while (dwBytesTransferred == 1);
        if(pCSerialCommHelper->m_usBufSize!=0)
        {
            CProtocolPacket *pCProtocolPacket = new CProtocolPacket(0,2048);
            pCProtocolPacket->AddBody(pCSerialCommHelper->m_usBufSize,(unsigned char*)pCSerialCommHelper->m_strBuffer.c_str());
            pCSerialCommHelper->m_pCPhysicalLayer->Data_ind(pCProtocolPacket);
            delete pCProtocolPacket;
        }
            ReleaseMutex(pCSerialCommHelper->m_hPortMutex);
        Sleep(2);
    }
    ExitThread(0);


    return 0;
}

これは、ファイルとミューテックスを作成する方法です

    m_pPortHandle = CreateFile(m_strPortName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
                         OPEN_EXISTING,NULL, NULL );
if (m_pPortHandle == INVALID_HANDLE_VALUE)

    {
        return E_HANDLE;
        //throw failure
    }

m_hPortMutex = CreateMutex(NULL,TRUE,L"MY_MUTEX");


if( m_hReadThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ReadThread,(LPVOID)this,0,&m_dwThreadID))
{
}
else
{
    return E_FAIL;
}
return S_OK;

ただし、文字列をポートに書き込んだ後、書き込み関数はミューテックスを正常に解放していますが、読み取りスレッドはまだ待機しています。

4

4 に答える 4

2

ミューテックスを作成するときは、bInitialOwner引数を TRUE に設定します。したがって、この時点でミューテックスはメインスレッドによって所有されています。

m_hPortMutex = CreateMutex(NULL,TRUE,L"MY_MUTEX");

ReadThread次に、mutex を取得しようとする を作成します。これは明らかに、メイン スレッドが解放するまでブロックされます。

WaitForSingleObject(pCSerialCommHelper->m_hPortMutex,INFINITE);

メイン スレッドが何かを書き込もうとすると、最初にミューテックスを再度取得しようとします。

WaitForSingleObject(m_hPortMutex,INFINITE);

メイン スレッドは既にミューテックスを所有しているため、この呼び出しはブロックせずにすぐに戻りますが、この段階では、メイン スレッドでミューテックスを 2 回取得しています (1 回はCreateMutex呼び出しで、2 回目は でWaitForSingleObject)。

ファイルへの書き込みが終了したら、次の呼び出しでミューテックスを解放します。

if(!ReleaseMutex(m_hPortMutex))

しかし、それはそれを一度しか解放しないので、まだメインスレッドによって所有されており、読み取りスレッドは引き続きブロックされます。

肝心なのは、ミューテックスを作成するときにbInitialOwnerパラメータを FALSE に設定する必要があるということです。

m_hPortMutex = CreateMutex(NULL,FALSE,L"MY_MUTEX");

CreateMutex のドキュメントを引用する:

ミューテックスを所有するスレッドは、その実行をブロックすることなく、繰り返しの待機関数呼び出しで同じミューテックスを指定できます。[...] ただし、所有権を解放するには、ミューテックスが待機を満たすたびに、スレッドは ReleaseMutex を 1 回呼び出す必要があります。

于 2013-06-24T14:23:22.620 に答える
1

あなたのコードには、次の問題があります。

  • ミューテックスは、コンストラクターを呼び出したスレッドによって最初に所有されます。
  • Write()エラー時にミューテックスを解放しません。

Write()コンストラクター呼び出しと同じスレッドから呼び出していると思いますが、これは既にミューテックスを所有しているため機能します。この後、ミューテックスはまだスレッドによって所有されています。もう一方のスレッド ブロックを作成します。

リリース呼び出しは、書き込みが成功したかどうかのチェックの外に移動することをお勧めします (常にリリースする必要があるため)。

を使用しReleaseMutex()てミューテックスを取得するたびに、またはミューテックスの呼び出しが成功するたびに、1 回呼び出す必要があることに注意してください。CreateMutex()bInitialOwnerTRUEWaitForSingleObject()

于 2013-06-24T13:12:23.100 に答える
1

問題は ReadThread の Readfile 関数にあります。正しく返されません。理由はわかりません。

void * CSerialCommHelper::ReadThread(void * pObj)
{
    CSerialCommHelper *pCSerialCommHelper =(CSerialCommHelper *)pObj; 
    DWORD dwBytesTransferred =0;
    char byte[1];
    string buffer;

    while (pCSerialCommHelper->m_pPortHandle != INVALID_HANDLE_VALUE)
    {

        WaitForSingleObject(pCSerialCommHelper->m_hPortMutex,INFINITE);
        do
        {
           dwBytesTransferred = 0;
                bool bReadResult= false;

                 bReadResult = ReadFile (pCSerialCommHelper->m_pPortHandle,byte,1,&dwBytesTransferred,NULL);


                if (dwBytesTransferred == 1)
                {
                    buffer.push_back(byte[0]);
                    continue;

                }


        }
        while (dwBytesTransferred == 1);

            pCSerialCommHelper->m_pCPhysicalLayer->Data_ind((unsigned char *)buffer.c_str());
            ReleaseMutex(pCSerialCommHelper->m_hPortMutex);

    }
    ExitThread(0);


    return 0;
}

コードをこのスニペットに置き換えてみたところ、bReadResult が適切に更新されていないことがわかりました。

于 2013-06-25T13:05:00.200 に答える