在SerialCommunication C ++中使用Mutex时遇到multithreading问题

我正在开发一个使用Windowsfunction的串行通信软件。 在这个CSerialCommhelper是处理所有串行通信function的类,而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; } 

这是我如何创build文件和互斥

  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; 

但是在向端口写入string之后,写入函数正在释放互斥锁,但读取线程仍在等待。

在创建互斥锁时,将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一次,互斥量满足等待。

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

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

我猜你是从同一个线程调用Write()作为构造函数的调用,这是因为它已经拥有互斥体。 在这之后,互斥量仍然由线程拥有。 使另一个线程阻塞。

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

您应该注意,每次获取互斥锁时需要调用一次ReleaseMutex() ,或者通过将bInitialOwner设置为TRUECreateMutex() ,或者通过互斥体上的成功WaitForSingleObject()调用来调用它。

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没有得到正确的更新。

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