ACE
中的守卫用于自动获取和释放锁。守卫类的对象定义一个代码块,在其上获取一个锁。在退出此代码块时,锁被自动释放。ACE
中的守卫类是一种模板,它通过所需锁定机制的类型来参数化。底层的锁可以是ACE Lock类属中的任何类,也就是,任何互斥体或锁类。它是这样工作的:对象的构造器获取锁,析构器释放锁。表4-2列出了ACE中可用的守卫:
|
名字 |
描述 |
|
ACE_Guard |
自动在底层锁上调用 acquire()和release()。任何ACE Lock类属中的锁都可以作为它的模板参数传入。 |
|
ACE_Read_Guard |
自动在底层锁上调用 acquire()和release()。 |
|
ACE_Write_Guard |
自动在底层锁上调用 acquire()和release()。 |
表
4-2 ACE中的守卫
下面的例子演示怎样使用看守:
例
4-5#include "ace/Synch.h"
#include "ace/Thread.h"
//Arguments that are to be passed to the worker thread are passed
//through this class.
class Args
{
public:
Args(int iterations):
mutex_(),iterations_(iterations){}
ACE_Thread_Mutex mutex_;
int iterations_;
};
//The starting point for the worker threads
static void* worker(void*arguments)
{
Args *arg= (Args*) arguments;
for(int i=0;i
{
ACE_DEBUG((LM_DEBUG,"(%t) Trying to get a hold of this iteration\n"));
ACE_Guard
guard(arg->mutex_); {
//This is our critical section
ACE_DEBUG((LM_DEBUG,"(%t) This is iteration number %d\n",i));
//work
ACE_OS::sleep(2);
}//end critical section
}
return 0;
}
int main(int argc, char*argv[])
{
//same as previous example
}
在上面的例子中,守卫在工作者线程中管理临界区。守卫对象从
ACE_Guard模板类创建而来。守卫应使用的锁的类型被传给模板。通过将所需获取的实际锁对象经由守卫对象的构造器传入,程序创建守卫对象。该锁由ACE_Guard在内部自动获取,所以for循环中的区域就是受保护的临界区。一旦出了作用域,看守对象就会被自动删除,锁也随之被释放。守卫是很有用的,因为它们保证一旦你获取了一个锁,你总是会释放它(当然,除非你的线程因为无法预料的情况而死掉)。在有许多不同的返回路径的复杂方法中,这被证明为是极其有用的。
4.2.3 ACE条件(Condition)类属
ACE_Condition
类是针对OS条件变量原语的包装类。那么,到底什么是条件变量呢?线程常常需要特定条件被满足才能继续它的操作。例如,设想线程需要在全局消息队列里插入消息。在插入任何消息之前,它必须检查在消息队列里是否有空闲空间。如果消息队列在“满”状态,它就什么也不能做,而必须进行休眠,过一会再重试。就是说,在访问全局资源之前,某个条件必须为真。然后,当另外的线程空出消息队列时,应该有方法通知或发信号给原来的线程:在消息队列里有位置了,现在应该再次尝试插入消息。这可以使用条件变量来完成。条件变量不是被用作互斥原语,而是用作特定条件已经满足的指示器。
在使用条件变量时,你的程序应该完成以下步骤:
需要特别注意的是,在阻塞在
wait调用中之前,条件变量机制(也就是ACE_Cond)负责释放全局资源上的互斥体。如果没有进行此操作,将没有其他的线程能够在此资源上工作(该资源是条件改变的原因)。同样,一旦阻塞线程收到信号、重又醒来,它在检查条件之前会在内部重新获取锁。下面的例子是对本章第一个例子的改写。如果你还记得,我们曾说过使用
ACE_Thread::join()调用可以使主线程等待其他的线程结束。另一种达到同样目的的方法是使用条件变量,它使主线程在退出之前等待“所有线程已经结束”条件为真。最后一个线程可以通过条件变量发信号给等待中的主线程,通知它所有线程已经结束、而它是最后一个。随后主线程继续执行,退出应用并销毁进程。如下如示:
例
4-6#include "ace/Thread.h"
#include "ace/Synch.h"
static int number=0;
static int seed=0;
class Args
{
public:
Args(ACE_Condition
cond_(cond), threads_(threads){}
ACE_Condition
int threads_;
};
static void* worker(void *arguments)
{
Args *arg= (Args*)arguments;
ACE_DEBUG((LM_DEBUG,"Thread (%t) Created to do some work\n"));
::number++;
//Work
ACE_OS::sleep(ACE_OS::rand()%2);
//Exiting now
ACE_DEBUG((LM_DEBUG,
"\tThread (%t) Done! \n\tThe number is now: %d\n",number));
//If all threads are done signal main thread that
//program can now exit
if(number==arg->threads_)
{
ACE_DEBUG((LM_DEBUG,
"(%t) Last Thread!\n All threads have done their job!
Signal main thread\n"));
arg->cond_->signal();
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc<2)
{
ACE_DEBUG((LM_DEBUG,
"Usage: %s
\n", argv[0])); ACE_OS::exit(1);
}
int n_threads=ACE_OS::atoi(argv[1]);
//Setup the random number generator
ACE_OS::srand(::seed);
//Setup arguments for threads
ACE_Thread_Mutex mutex;
ACE_Condition
Args arg(&cond,n_threads);
//Spawn off n_threads number of threads
for(int i=0; i { if(ACE_Thread::spawn((ACE_THR_FUNC)worker,(void*)&arg, THR_DETACHED|THR_NEW_LWP)==-1) ACE_DEBUG((LM_DEBUG,"Error in spawning thread\n")); } //Wait for signal indicating that all threads are done and program //can exit. The global resource here is “number” and the condition //that the condition variable is waiting for is number==n_threads. mutex.acquire(); while(number!=n_threads) cond.wait(); ACE_DEBUG((LM_DEBUG,"(%t) Main Thread got signal. Program exiting..\n")); mutex.release(); ACE_OS::exit(0); }
注意主线程首先获取一个互斥体,然后对条件进行测试。如果条件不为真,主线程就等待在此条件变量上。条件变量随即自动释放互斥体,并使主线程进入睡眠。条件变量总是像这样与互斥体一起使用。这是一种可如下描述的一般模式
[1]:
while( expression NOT TRUE ) wait on condition variable;
记住条件变量不是用于互斥,而是用于我们所描述的发送信号功能。
除了
ACE_Condition类,ACE还包括ACE_Condition_Thread_Mutex类,它使用ACE_Thread_Mutex作为全局资源的底层锁定机制。
