我尝试了几种方法来同步以下场景。我尝试的最后一种方法是使用事件在FifoQueueData();之后发出信号。然后,线程2将从事件中释放并通过网络发送数据。问题是线程1绕过太快并且在线程2等待同一事件之前发出事件信号。这会导致事件等到它超时。
thread1 {
for(1 .. 10) {
GenerateData();
FifoQueueData();
}
signal();
}
thread2 {
while(not signalled) {
WaitForQueuedData();
FifoDequeueData();
SendDataOverNetwork();
}
}
答案 0 :(得分:1)
因为你在Windows上,你确实可以使用线程消息队列进行同步(这个特定队列是线程安全的,也许消息队列是Olaf所指的"通常")。为此,请使用GetMessage
和PostThreadMessage
。
答案 1 :(得分:0)
我尝试的最后一种方法是使用事件在FifoQueueData();之后发出信号。然后,线程2将从事件中释放并通过网络发送数据。问题是线程1绕过太快并且在线程2等待同一事件之前发出事件信号。这会导致事件等到它超时。
如果您要使用事件,请使用2个手动重置事件和线程安全锁,如关键部分。当队列有可用空间时,一个事件发出信号,当队列有数据时,一个事件发出信号。在最终解锁队列之前,Thread1将等待可用事件发出信号,锁定队列,将数据放入其中,设置数据事件,并在队列已满时重置可用事件。在最终解锁队列并处理项目之前,Thread2将等待Data事件,锁定队列,使项目出列,设置Available事件,并在队列为空时重置Data事件。
CRITICAL_SECTION csQueue;
HANDLE hAvailableEvent, hDataEvent;
BOOL bFinished;
...
InitializeCriticalSection(&csQueue);
hAvailableEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
hDataEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
bFinished = FALSE;
...
void FifoQueueData(BOOL IsFinished)
{
WaitForSingleObject(hAvailableEvent, INFINITE);
EnterCriticalSection(&csQueuelock);
if (IsFinished)
bFinished = TRUE;
else
{
// put data in the queue
if (queue is full)
ResetEvent(hAvailableEvent);
}
SetEvent(hDataEvent);
LeaveCriticalSection(&csQueuelock);
}
BOOL FifoDequeueData()
{
WaitForSingleObject(hDataEvent, INFINITE);
EnterCriticalSection(&csQueuelock);
if ((queue is empty) && (bFinished))
{
LeaveCriticalSection(&csQueuelock);
return FALSE;
}
// remove data from the queue
SetEvent(hAvailableEvent);
if (queue is empty)
ResetEvent(hDataEvent);
LeaveCriticalSection(&csQueuelock);
}
thread1
{
bFinished = FALSE;
for(1 .. 10)
{
GenerateData();
FifoQueueData(FALSE);
}
FifoQueueData(TRUE);
}
thread2
{
while (FifoDequeueData())
SendDataOverNetwork();
}
另一种选择是使用I/O Completion Port。在启动线程之前调用CreateIoCompletionPort()
,然后使用PostQueuedCompletionStatus()
使用Thread1队列数据,使用GetQueuedCompletionStatus()
将Thread2队列数据。 IOCP使用FIFO队列并且是自同步的。
HANDLE hQueue;
...
hQueue = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
...
void FifoQueueData(BOOL IsFinished)
{
if (IsFinished)
PostQueuedCompletionStatus(hQueue, 0, 1, NULL);
else
{
LPBYTE buffer = LocalAlloc(LMEM_FIXED, size of data);
if (buffer)
{
// copy data into buffer...
if (!PostQueuedCompletionStatus(hQueue, size of data, 0, (LPOVERLAPPED)buffer))
LocalFree(buffer);
}
}
}
BOOL FifoDequeueData()
{
DWORD dwSize = 0;
ULONG_PTR ulCompletionKey = 0;
LPBYTE buffer;
if (!GetQueuedCompletionStatus(hQueue, &dwSize, &ulCompletionKey, (LPOVERLAPPED*)&buffer, INFINITE))
return FALSE;
if (ulCompletionKey == 1)
return FALSE;
// copy data from buffer up to dwSize bytes as needed...
LocalFree(buffer);
return TRUE;
}
thread1
{
for(1 .. 10)
{
GenerateData();
FifoQueueData(FALSE);
}
FifoQueueData(TRUE);
}
thread2
{
while (FifoDequeueData())
SendDataOverNetwork();
}