《Windows via C/C++》学习笔记 —— 用户模式的“线程同步”之“条件变量”

  Condition variables —— 条件变量,是Windows Vista中新增加的一种处理线程同步问题的机制。它可以与“关键代码段(critical section)”或“读写锁(SRWLock)”相互配合使用,来实现线程的同步,特别是实现类似“生产者-消费者”问题的时候,十分有效。

  如果当前没有“产品”可供“消费者线程”(读者线程)使用,那么该“消费者线程”要释放掉对应的读写锁或者关键代码段,然后等待直到有一个新的“产品”被“生产者线程”(写者线程)制造出来之后,方可继续运行。

  如果一个用来存放“产品”的数据结构满了(比如数组),那么对应“生产者线程”需要释放有关的锁和关键代码段,同时要等待“消费线程者”消费完这些“产品”。

 

  条件变量机制就是为了简化上述“生产者-消费者”问题而设计的一种线程同步机制。当一个线程需要以原子的方式释放加在资源上的锁,并且需要被阻塞运行直到一个条件被满足,你可以呼叫如下的函数:

BOOL SleepConditionVariableCS(
   PCONDITION_VARIABLE pConditionVariable,
   PCRITICAL_SECTION pCriticalSection,
   DWORD dwMilliseconds);
BOOL SleepConditionVariableSRW(
   PCONDITION_VARIABLE pConditionVariable,
   PSRWLOCK pSRWLock,
   DWORD dwMilliseconds,
   ULONG Flags);

 

  正如函数名所预示的那样,第一个函数针对关键代码段,第二个函数针对读写锁。

  第1个参数pContidionVariable参数指向一个初始化的条件变量,该条件变量指明了调用者(一个线程)的条件变量,参数类型是CONDITION_VARIABLE的指针。

  第2个参数指明了一个“关键代码段”或“读写锁”,它们是用来保护共享资源的。

  第3个参数dwMilliseconds指明了你的线程想要等待多长时间,你可以传递INFINITE,指明需要无限期等待下去。

  第二个函数的第4个参数Flags指明当条件变量满足的时候,你需要对应的锁做如何的要求(即返回的时候设置锁,该锁应该是什么类型的):在“生产者线程”(写者线程)中你应该传递0给该参数指明该锁是一个“排他锁”,该锁被线程独占;在“消费者线程”(读者线程)中你应该传递CONDITION_VARIABLE_LOCKMODE_SHARED给该参数指明该锁是一个“共享锁”,该锁能以共享的方式为“消费者线程”服务。

  在该参数调用的时候,第二个参数所指定的关键代码段或读写锁会被释放,使得对应的线程可以访问共享资源,从而去“生产”或“消费”;在该函数返回的时候,这个锁又会被设置。如果是SRWLock,该函数返回的时候根据Flags参数设置读写锁类型:排他锁或共享锁。关键代码段则会被自动设置,因为关键代码段总是“排他”的。

  如果等待超时,该函数返回FALSE,否则返回TRUE。

 

  一个线程当被SleepConditionVariableCS或SleepConditionVariableSRW阻塞之后,可以被另一个线程通过呼叫WakeConditionVariable或WakeAllConditionVariable函数唤醒。

VOID WakeConditionVariable(
   PCONDITION_VARIABLE ConditionVariable);     
// 条件变量指针
VOID WakeAllConditionVariable(
   PCONDITION_VARIABLE ConditionVariable);     
// 条件变量指针

 

  当你呼叫WakeConditionVariable函数的时候,传递一个条件变量的指针给它,此时,在一个等待在同样条件变量的上的线程的SleepConditionVariable函数内部,条件变量收到信号,通知线程,该函数会返回,同时把对应的锁设定为所需要的类型。

  当你呼叫WakeAllConditionVarialbe函数的时候,一个过多个等待在相同条件变量上的线程会被被唤醒。唤醒多个线程是可以的,但是你需要在调用SleepConditionVariable*函数的时候设定参数Flags:给“生产者线程”传递0;给“消费者线程”传递CONDITION_VARIABLE_LOCKMODE_SHARED。所以,有些时候,“消费者线程”全部会被唤醒。或者这样唤醒:生产者、消费者、生产者、消费者……如此循环。

 

  本书还举了一个例子,这里就不多说了,我个人总结了下,运用条件变量应该遵循如下模式(自己是这么认为的,如果有误还大家请指出)

 

CONDITION_VARIABLE g_cvProduce;   //生产条件变量
CONDITION_VARIABLE g_cvConsume;   //消费条件变量
SRWLOCK g_srwLock;     //读写锁

DWORD WINAPI Consumer(PVOID pvParam)     
//消费者线程函数
{
     
     AcquireSRWLockShard(
&g_srwLock);     //请求共享锁(读锁)
     SleepConditionVariableSRW(g_cvConsume, &g_srwLock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);    //等待条件变量,会被生产者线程唤醒

          
//消费

     ReleaseSRWLockShared(
&g_srwLock);     //释放共享锁
     WakeConditionVariable(&g_cvProduce);     //唤醒一个生产者线程
     
}

DWORD WINAPI Producer(PVOID pvParam)     
//生产者线程函数
{
     
     AcquireSRWLockExclusive(
&g_srwLock);     //要求一个排他锁(写锁)

 

     //等待条件变量受信,会被消费者线程唤醒
     SleepConditionVariableSRW(g_cvProduce, &g_srwLock, INFINITE, 0);

          
//生产

     ReleaseSRWLockExclusive(
&g_srwLock);     //释放排他锁
     WakeAllConditionVariable(&g_cvConsume);  //唤醒所有消费者线程
     
}

你可能感兴趣的:(windows)