在SerialCommunication C ++中使用Mutex时出现多线程问题

时间:2013-06-24 11:34:25

标签: windows multithreading visual-c++ serial-port mutex

我正在使用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包含一个CphysicalLayer指针,用于在收到数据时通知physicalLayer。

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;

但是在将字符串写入端口后,write函数正在成功释放互斥锁,但读取线程仍在等待。

4 个答案:

答案 0 :(得分:2)

创建互斥锁时,将 bInitialOwner 参数设置为TRUE。所以在这一点上,互斥锁由主线程拥有。

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

然后创建ReadThread,尝试获取互斥锁。这显然会在主线程释放它之前阻塞。

WaitForSingleObject(pCSerialCommHelper->m_hPortMutex,INFINITE);

当主线程尝试写东西时,它首先要做的是尝试再次获取互斥锁。

WaitForSingleObject(m_hPortMutex,INFINITE);

由于主线程已拥有互斥锁,此调用将立即返回而不会阻塞,但在此阶段,您现在已经在主线程中获取了两次互斥锁(一次在CreateMutex调用中,第二次与WaitForSingleObject)。

当您完成对文件的写入后,您将通过此调用释放互斥锁:

if(!ReleaseMutex(m_hPortMutex))

但是只发布一次,所以它仍然由主线程拥有,而读线程将继续阻塞。

最重要的是,在创建互斥锁时,应将 bInitialOwner 参数设置为FALSE。

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

引用CreateMutex documentation

  

拥有互斥锁的线程可以在重复的等待函数调用中指定相同的互斥锁,而不会阻止其执行。 [...]但是,要释放其所有权,每次互斥锁满足等待时,线程必须调用一次ReleaseMutex。

答案 1 :(得分:1)

在您的代码中,我看到以下问题:

  • 互斥锁最初由调用构造函数的线程拥有。
  • Write()在错误时不会释放互斥锁。

我猜你从构造函数调用的同一个线程中调用Write(),这是因为它已经拥有了互斥锁。在此之后,互斥锁仍归线程所有。使另一个线程阻塞。

我建议您在检查之外移动释放调用是否写入成功(因为您总是需要释放它)。

您应该注意,每次获得互斥锁时都需要调用ReleaseMutex() CreateMutex()bInitialOwner设置为TRUE,或成功{ {1}}调用互斥锁。

答案 2 :(得分: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未正确更新。

答案 3 :(得分:0)

尝试创建没有名称的互斥锁。命名互斥锁通常用于进程间通信。通常,用于进程内线程之间的通信的互斥锁不会被命名 - 它们不需要按名称打开,因为所有进程线程都可以访问互斥锁句柄。