如何同步以下场景?

时间:2015-08-29 00:27:26

标签: c winapi synchronization

我尝试了几种方法来同步以下场景。我尝试的最后一种方法是使用事件在FifoQueueData();之后发出信号。然后,线程2将从事件中释放并通过网络发送数据。问题是线程1绕过太快并且在线程2等待同一事件之前发出事件信号。这会导致事件等到它超时。

thread1 {
  for(1 .. 10) {
    GenerateData();
    FifoQueueData();
  }
  signal();
}

thread2 {
  while(not signalled) {
    WaitForQueuedData();
    FifoDequeueData();
    SendDataOverNetwork();
  }
}

2 个答案:

答案 0 :(得分:1)

因为你在Windows上,你确实可以使用线程消息队列进行同步(这个特定队列是线程安全的,也许消息队列是Olaf所指的"通常")。为此,请使用GetMessagePostThreadMessage

但是,请注意,可以从其他代码访问消息队列 - 为了类型安全,您可以将对象放在自己的队列中,并仅使用线程消息队列来唤醒其他线程。 (要了解为什么这很重要,请阅读"窗口破碎攻击")。在这种情况下,您可能只是使用自动重置事件。

答案 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();
}