代码改变世界

线程---条件变量,一次性初始化

2011-05-18 22:57  Clingingboy  阅读(1796)  评论(0编辑  收藏  举报

 

以SDK的生产者和消费者为例,两个是一个循环关系.

生产者的职责:

1.负责生产产品,如果产品的产量达到最高量了,就停止生产(没有消费者用,当然停掉了),即生产线暂时停工
2.当有消费者购买时,未达到最高产品数量的时候,则继续生产.

以上可以看到生产者生产东西是有条件的,即有人消费(废话,卖东西肯定得有人买了,有人购买就是生产的触发条件)

归纳为

生产者做了以下事情

1.东西卖不出去,停止生产
2.通知消费者,我有货快来买
3.有消费者买了产品,继续生产到最高产品数量

三者不断循环

同理消费者的逻辑也相似

1.没有产品,停止使用
2.通知生产者,东西卖完了,赶紧补货
3.生产者生产好产品,消费者继续使用

三者不断循环

那么假设生产者和消费者,各自为一个线程,当某些条件满足时就会阻塞自己(如东西卖不出去,停止生产;没有产品,停止使用),当打破这些条件时则继续执行,在现实生活中这很容易理解,换成代码比较晦涩.下面来看代码

先定义三个变量

CONDITION_VARIABLE BufferNotEmpty;
CONDITION_VARIABLE BufferNotFull;
CRITICAL_SECTION   BufferLock;

必须要对变量进行初始化调用,然后有1个生产者和2个消费者,条件变量依赖于临界区(CRITICAL_SECTION)

InitializeConditionVariable (&BufferNotEmpty);
InitializeConditionVariable (&BufferNotFull);

InitializeCriticalSection (&BufferLock);

DWORD id;
HANDLE hProducer1 = CreateThread (NULL, 0, ProducerThreadProc, (PVOID)1, 0, &id);
HANDLE hConsumer1 = CreateThread (NULL, 0, ConsumerThreadProc, (PVOID)1, 0, &id);
HANDLE hConsumer2 = CreateThread (NULL, 0, ConsumerThreadProc, (PVOID)2, 0, &id);

来看生产者代码:

DWORD WINAPI ProducerThreadProc (PVOID p)
{
    ULONG ProducerId = (ULONG)(ULONG_PTR)p;

    while (true)
    {
        // Produce a new item.

        Sleep (rand() % PRODUCER_SLEEP_TIME_MS);

        ULONG Item = InterlockedIncrement (&LastItemProduced);

        EnterCriticalSection (&BufferLock);

        while (QueueSize == BUFFER_SIZE && StopRequested == FALSE)
        {
            // Buffer is full - sleep so consumers can get items.
            SleepConditionVariableCS (&BufferNotFull, &BufferLock, INFINITE);
        }

        if (StopRequested == TRUE)
        {
            LeaveCriticalSection (&BufferLock);
            break;
        }

        // Insert the item at the end of the queue and increment size.

        Buffer[(QueueStartOffset + QueueSize) % BUFFER_SIZE] = Item;
        QueueSize++;
        TotalItemsProduced++;

        printf ("Producer %u: item %2d, queue size %2u\r\n", ProducerId, Item, QueueSize);

        LeaveCriticalSection (&BufferLock);

        // If a consumer is waiting, wake it.

        WakeConditionVariable (&BufferNotEmpty);
    }

    printf ("Producer %u exiting\r\n", ProducerId);
    return 0;
}

当数量等于BUFFER_SIZE则罢工,否则的话生产出产品就通知消费者有货

while (QueueSize == BUFFER_SIZE && StopRequested == FALSE)
{
    // Buffer is full - sleep so consumers can get items.
    SleepConditionVariableCS (&BufferNotFull, &BufferLock, INFINITE);
}
// If a consumer is waiting, wake it.

WakeConditionVariable (&BufferNotEmpty);

消费者也是如此,没货就等待,当用完一个产品就通知生产者继续生产

while (QueueSize == 0 && StopRequested == FALSE)
{
    // Buffer is empty - sleep so producers can create items.
    SleepConditionVariableCS (&BufferNotEmpty, &BufferLock, INFINITE);
}
WakeConditionVariable (&BufferNotFull);
DWORD WINAPI ConsumerThreadProc (PVOID p)
{
    
    ULONG ConsumerId = (ULONG)(ULONG_PTR)p;

    while (true)
    {
        EnterCriticalSection (&BufferLock);

        while (QueueSize == 0 && StopRequested == FALSE)
        {
            // Buffer is empty - sleep so producers can create items.
            SleepConditionVariableCS (&BufferNotEmpty, &BufferLock, INFINITE);
        }

        if (StopRequested == TRUE && QueueSize == 0)
        {
            LeaveCriticalSection (&BufferLock);
            break;
        }

        // Consume the first available item.

        LONG Item = Buffer[QueueStartOffset];

        QueueSize--;
        QueueStartOffset++;
        TotalItemsConsumed++;

        if (QueueStartOffset == BUFFER_SIZE)
        {
            QueueStartOffset = 0;
        }

        printf ("Consumer %u: item %2d, queue size %2u\r\n", 
            ConsumerId, Item, QueueSize);

        LeaveCriticalSection (&BufferLock);

        // If a producer is waiting, wake it.

        WakeConditionVariable (&BufferNotFull);

        // Simulate processing of the item.

        Sleep (rand() % CONSUMER_SLEEP_TIME_MS);
    }

    printf ("Consumer %u exiting\r\n", ConsumerId);
    return 0;
}

 

附带Windows核心编程第5版(采用新api)以前的线程池资料

http://wenku.baidu.com/view/3df5000d6c85ec3a87c2c5c0.html

一次性初始化

以保证线程在调用资源时,确保资源已经被初始化,并且只初始化一次

InitHandleFunction 函数将只会触发一次,初始化的对象估计会与INIT_ONCE对象关联起来

// Global variable
 INIT_ONCE g_InitOnce;
 
 BOOL CALLBACK InitHandleFunction (
     PINIT_ONCE InitOnce,
     PVOID Parameter,
     PVOID *lpContext)
 {
 
   HANDLE hEvent;
 
   hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
 
   if (NULL == hEvent)
   {
     return FALSE;
   }
   else
   {
     *lpContext = hEvent;
     return TRUE;
   }
 }
 
 HANDLE OpenEventHandleSync()
 {
   PVOID lpContext;
   BOOL  bStatus;
   
   bStatus = InitOnceExecuteOnce(&g_InitOnce,
                                 InitHandleFunction,
                                 NULL,
                                 &lpContext);
 
   if (bStatus)
   {
     return (HANDLE)lpContext;
   }
   else
   {
     return (INVALID_HANDLE_VALUE);
   }
 }

异步初始化:

其甚至没有回调函数,并且前后有两个InitOnceBeginInitialize函数,用InitOnceComplete来通知初始化完毕

// Global variable
  INIT_ONCE g_InitOnce;
  
  HANDLE OpenEventHandleAsync()
  {
    PVOID  lpContext;
    BOOL   fStatus;
    BOOL   fPending;
    HANDLE hEvent;
    
    fStatus = InitOnceBeginInitialize(&g_InitOnce,
                                      INIT_ONCE_ASYNC,
                                      &fPending,
                                      &lpContext);  
  
    if (!fStatus)
    {
      return (INVALID_HANDLE_VALUE);
    }
  
    // Initialization has already completed.
    if (!fPending)
    {
      return (HANDLE)lpContext;
    }
  
    hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
    if (NULL == hEvent)
    {
      return (INVALID_HANDLE_VALUE);
    }
  
  
    fStatus = InitOnceComplete(&g_InitOnce,
                               INIT_ONCE_ASYNC,
                               (PVOID)hEvent);
  
    if (fStatus)
    {
      return hEvent;
    }
    
    // Initialization has already completed. Free the local event.
    CloseHandle(hEvent);
  
  
    // Retrieve the final context data.
    fStatus = InitOnceBeginInitialize(&g_InitOnce,
                                      INIT_ONCE_CHECK_ONLY,
                                      &fPending,
                                      &lpContext);
  
    if (fStatus && !fPending)
    {
      return (HANDLE)lpContext;
    }
    else
    {
      return INVALID_HANDLE_VALUE;
    }
  }

参考:
http://msdn.microsoft.com/zh-cn/magazine/cc163405.aspx#S3