CreateEvent(...)函数在线程通信中的秘密!
最近我在研读一份非常庞大的代码,其中有部分关于线程同步的技术,看了很久总要知道了其中的奥秘,也许对于看官来说可能很easy并认为本人好笨,不管怎么说吧笨人反正已经晓得了这个技术技巧啊。
情况是这样的:
(1) 在一个表示某种设备的C++类代码,其中创建了一个命名的“事件对象”
bool SomeDevide::Open( int iPortNumber )
{
。。。
// Create the event that will get Set when data comes in for us to read
m_hReadEvent= CreateEvent(NULL,FALSE,FALSE,L"Data Available for read");
。。。
}
(2) 该设备类为了不断的和底层硬件通信(通过一个串口类实现)必须启动一个线程来读取设备发来的数据或者控制设备的状态。奇怪的是我光发现在该线程中使用了 WaitForMultipleObjects(。。。)来等待(1)中创建的事件,但是在设备类中没有发现那句语句来设置该“事件”为有信号。
/
Waits for data arrival event to be signalled from serial class instance in m_Serial
Once it gets notified of data arrival, it tries to read 128 chars
*/
static DWORD DevideWaitForReadThread( LPVOID pIn )
{
HANDLE hEvents[2];
hEvents[0] =m_hReadEvent;
hEvents[1] = m_hStopEvent;
。。。
// LOOP 1 : Wait for data to arrive and read it.
do
{
DWORD dwEvent = WaitForMultipleObjects( 2, hEvents, FALSE, 1000 );
。。。
}
(3)最后才发现是通过 设备类 所依赖的 串口类 来向该“事件对象”发送信号的。这里面就用到了“命名事件对象”的技术了;如果在你的程序中有多处使用CreateEvent(。。。)创建同名的事件对象,那么在这些语句中只有第一次执行CreateEvent时windows系统才真正在内核中创建该事件的内核对象,后续语句都是返回先前创建的事件对象句柄。
【注】
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPTSTR lpName );
Return Values
A handle to the event object indicates success.If the named event object existed before the function call, the function returns a handle to the existing objectand GetLastError returns ERROR_ALREADY_EXISTS. NULL indicates failure. To get extended error information, call GetLastError.
If lpName matches the name of an existing semaphore, mutex, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same name space.
我将串口类中的构造函数列于下面以供参考:
EmbeddedSerial::EmbeddedSerial()
{
。。。
/ Events to stop the read thread /
m_hStopReadThread = CreateEvent( NULL, FALSE, FALSE, L"Stop Read Thread" );
/ Events to signal that the read thread have stopped /
m_hReadThreadDown = CreateEvent( NULL, FALSE, FALSE, L"Read thread has shut down" );
m_hReadEvent= CreateEvent(NULL,FALSE,FALSE,L"Data Available for read");
。。。
}
以下是EmbededSerial类中提供的一个 线程入口函数:
static DWORD ReadThread( LPVOID lp );
该函数的定义如下:
DWORD EmbeddedSerial::ReadThread( LPVOID lp )
{
((EmbeddedSerial*)lp)->ReadThreadObj();
return 0;
}
该函数中调用的ReadThreadObj()定义如下:
/
The thread responsible for reading data. Waits for one of 2 events
An internal event to signal data arrival at the port - in which case it reads all available data from port into ring buffer
m_hStopReadThread - in which case th thread ends
*/
void EmbeddedSerial::ReadThreadObj( void )
{
。。。
DWORD dwWaitMask = EV_RXCHAR;
DWORD dwError =0;
unsigned char cTemp[READ_BLOCK_SIZE];
bool bLoop = true;
SetCommMask(m_hCommPort, dwWaitMask );
do
{
if (WaitForSingleObject( m_hStopReadThread, 100) == WAIT_OBJECT_0)
{
// Quit the thread ??
bLoop = false;
}
else
{
DWORD dwMask;
if( GetCommMask( m_hCommPort, &dwMask ) )
{
if( EV_RXCHAR == (dwMask & EV_RXCHAR) )
{
// Read data in blocks of 128 bytes
do
{
ReadFile( m_hCommPort, cTemp, READ_BLOCK_SIZE,&dwBytesRead,NULL);
if( dwBytesRead > 0 )
{
// update the last tick count of read data
m_TickCountAtLastRead = GetTickCount();
// Put the data into the ring buffer for Read data
AddDataToRingBuffer(cTemp, dwBytesRead);
// signal more data
SetEvent(m_hReadEvent); //此处将信号 传递到 设备类 中。
}
}
while (dwBytesRead > 0 );
}
}
}
}
while( bLoop ); // End do.
// Tell the dtr we have shut down
SetEvent( m_hReadThreadDown );
}
【注】所有整个线程的交换过程是这样的:
串口(硬件接收器)接收到数据后会置位串口的状态位(状态寄存器)----->接着串口驱动程序(C++类实现的串口驱动程序)中的读写线程使用if( GetCommMask( m_hCommPort, &dwMask ) )取得当前串口的状态,以便判断是否接收到了数据,如果接受到数据则将数据放入串口驱动所维护的缓冲区中,并且通过
SetEvent(m_hReadEvent);将数据已经准备好信号发生到 设备类中------->然后由设备类中启动的设备线程读取串口线程所送上来的数据并进行后续解析。
原文链接: https://www.cnblogs.com/vmyspace/archive/2012/01/30/2332217.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/40687
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!