您的位置:首页 > 编程语言 > C语言/C++

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

2008-08-09 21:04 531 查看
  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); //唤醒所有消费者线程

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐