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 包含一个 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尝试获取互斥锁的方法。这显然会阻塞,直到主线程释放它。

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 文档

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

于 2013-06-24T14:23:22.620 回答
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 回答
1

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

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

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

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

ReleaseMutex()您应该注意,每次获取互斥锁时都需要调用一次,方法CreateMutex()bInitialOwner设置为TRUE,或者成功WaitForSingleObject()调用互斥锁。

于 2013-06-24T13:12:23.100 回答
0

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

于 2013-06-24T12:34:32.880 回答