http://hi.baidu.com/653701wwww/blog/item/9d787c00e961bb8fe950cdb9.html
锁类属包含的类包装简单的锁定机制,比如互斥体、信号量、读/写互斥体和令牌。在这一类属中可用的类在表4-1 中显示。每个类名后都有对用法和用途的简要描述:
名字 |
描述 |
ACE_Mutex |
封装互斥机制(根据平台,可以是 mutex_t 、 pthread_mutex_t 等等)的包装类,用于提供简单而有效的机制来使对共享资源的访问序列化。它与二元信号量( binary semaphore )的功能相类似。可被用于线程和进程间的互斥。 |
ACE_Thread_Mutex |
可用于替换 ACE_Mutex ,专用于线程同步。 |
ACE_Process_Mutex |
可用于替换 ACE_Mutex ,专用于进程同步。 |
ACE_NULL_Mutex |
提供了 ACE_Mutex 接口的“无为”( do-nothing )实现,可在不需要同步时用作替换。 |
ACE_RW_Mutex |
封装读者/作者锁的包装类。它们是分别为读和写进行获取的锁,在没有作者在写的时候,多个读者可以同时进行读取。 |
ACE_RW_Thread_Mutex |
可用于替换 ACE_RW_Mutex ,专用于线程同步。 |
ACE_RW_Process_Mutex |
可用于替换 ACE_RW_Mutex ,专用于进程同步。 |
ACE_Semaphore |
这些类实现计数信号量,在有固定数量的线程可以同时访问一个资源时很有用。在 OS 不提供这种同步机制的情况下,可通过互斥体来进行模拟。 |
ACE_Thread_Semaphore |
应被用于替换 ACE_Semaphore ,专用于线程同步。 |
ACE_Process_Semaphore |
应被用于替换 ACE_Semaphore ,专用于进程同步。 |
ACE_Token |
提供“递归互斥体”( recursive mutex ),也就是,当前持有某令牌的线程可以多次重新获取它,而不会阻塞。而且,当令牌被释放时,它确保下一个正阻塞并等待此令牌的线程就是下一个被放行的线程。 |
ACE_Null_Token |
令牌接口的“无为”( do-nothing )实现,在你知道不会出现多个线程时使用。 |
ACE_Lock |
定义锁定接口的接口类。一个纯虚类,如果使用的话,必须承受虚函数调用开销。 |
ACE_Lock_Adapter |
基于模板的适配器,允许将前面提到的任意一种锁定机制适配到 ACE_Lock 接口。 |
表
4-1 ACE 锁类属中的类
表
4-1 中描述的类都支持同样的接口。但是,在任何继承层次中,这些类都是互不关联 的。在 ACE 中,锁通常用模板来参数化,因为,在大多数情况下,使用虚函数调用的开销都是不可接受的。使用模板使得程序员可获得相当程度的灵活性。他可以在编译时(但不是在运行时)选择他想要使用的的锁定机制的类型。然而,在某些情形中,程序员仍可能需要使用动态绑定和替换( substitution );对于这些情况, ACE 提供了 ACE_Lock 和 ACE_Lock_Adapter 类。
4.2.1.1
使用互斥体类互斥体实现了“互相排斥 ”(
任何线程在进入临界区之前,必须获取 (
什么时候需要使用互斥体呢?互斥体用于保护共享的易变代码,也就是,全局或静态数据。这样的数据必须通过互斥体进行保护,以防止它们在多个线程同时访问时损坏。
下面的例子演示
mutual exclusion )同步的简单形式(所以名为互斥体 (mutex) )。互斥体禁止多个线程同时进入受保护的代码“临界区 ”( critical section )。因此,在任意时刻,只有一个线程被允许进入这样的代码保护区。 acquire )与此区域相关联的互斥体的所有权。如果已有另一线程拥有了临界区的互斥体,其他线程就不能再进入其中。这些线程必须等待,直到当前的属主线程释放 ( release )该互斥体。 ACE_Thread_Mutex 类的使用。注意在此处很容易用 ACE_Mutex 替换 ACE_Thread_Mutex 类,因为它们拥有同样的接口。
例
4-2#include "ace/Synch.h"
#include "ace/Thread.h"
//Arguments that are to be passed to the worker thread are passed
//through this struct.
struct 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<arg->iterations_;i++)
{
ACE_DEBUG((LM_DEBUG,
"(%t) Trying to get a hold of this iteration/n"));
//This is our critical section
arg->mutex_.acquire();
ACE_DEBUG((LM_DEBUG,"(%t) This is iteration number %d/n",i));
ACE_OS::sleep(2);
//simulate critical work
arg->mutex_.release();
}
return 0;
}
int main(int argc, char*argv[])
{
if(argc<2)
{
ACE_OS::printf("Usage: %s <number_of_threads>
<number_of_iterations>/n", argv[0]);
ACE_OS::exit(1);
}
Args arg(ACE_OS::atoi(argv[2]));
//Setup the arguments
int n_threads = ACE_OS::atoi(argv[1]);
//determine the number of threads to be spawned.
ACE_thread_t *threadID = new ACE_thread_t[n_threads+1];
ACE_hthread_t *threadHandles = new ACE_hthread_t[n_threads+1];
if(ACE_Thread::spawn_n(threadID, //id’s for each of the threads
n_threads, //number of threads to spawn
(ACE_THR_FUNC)worker, //entry point for new thread
0, //args to worker
THR_JOINABLE | THR_NEW_LWP, //flags
ACE_DEFAULT_THREAD_PRIORITY,
0, 0, threadHandles)==-1)
ACE_DEBUG((LM_DEBUG,"Error in spawning thread/n"));
//spawn n_threads
for(int i=0; i<n_threads; i++)
ACE_Thread::join(threadHandles[i]);
//Wait for all the threads to exit before you let the main fall through
//and have the process exit.
return 0;
}
在上面的例子中,
在此例中,一开始,每个线程立即进入
ACE_Thread 包装类用于生成多个线程来执行 worker() 函数,就如同在前面的例子里一样。 Arg 对象作为参数传入各个线程,在该对象中含有循环要执行的次数,以及将要使用的互斥体。 for 循环。一进入循环,线程就进入了临界区。在临界区内完成的工作使用 ACE_Thread_Mutex 互斥体对象进行保护。该对象由主线程作为参数传给工作者线程。临界区控制是通过在 ACE_Thread_Mutex 对象上发出 acquire() 调用,从而获取互斥体的所有权来完成的。一旦互斥体被获取,没有其他线程能够再进入这一代码区。临界区控制是通过使用 release() 调用来释放的。一旦互斥体的所有权被放弃,就会唤醒所有其他在等待的线程。这些线程随即相互竞争,以获得互斥体的所有权。第一个试图获取所有权的线程会进入临界区。
4.2.1.2
将锁和锁适配器 ( Lock Adapter )用于动态绑定如前面所提到的,各种互斥体锁应被直接用于你的代码,或者,如果需要灵活性,作为模板参数来使用。但是,如果你需要动态地(也就是在运行时)改变你的代码所用锁的类型,就无法使用这些锁。
为应对这个问题,
下面的例子演示
ACE 拥有 ACE_Lock 和 ACE_Lock_Adapter 类,它们可用于这样的运行时替换( substitution )。 ACE_Lock 类和 ACE_Lock_Adapter 怎样为应用程序员提供方便,和锁定机制一起使用动态绑定和替换。
例
4-3#include "ace/Synch.h"
#include "ace/Thread.h"
//Arguments that are to be passed to the worker thread are passed
//through this class.
struct Args
{
public:
Args(ACE_Lock* lock,int iterations):
mutex_(lock),iterations_(iterations){}
ACE_Lock* mutex_;
int iterations_;
};
//The starting point for the worker threads
static void* worker(void*arguments)
{
Args *arg= (Args*) arguments;
for(int i=0;i<arg->iterations_;i++)
{
ACE_DEBUG((LM_DEBUG,
"(%t) Trying to get a hold of this iteration/n"));
//This is our critical section
arg->mutex_->acquire();
ACE_DEBUG((LM_DEBUG,"(%t) This is iteration number %d/n",i));
ACE_OS::sleep(2);
//simulate critical work
arg->mutex_->release();
}
return 0;
}
int main(int argc, char*argv[])
{
if(argc<4)
{
ACE_OS::printf("Usage: %s <number_of_threads>
<number_of_iterations> <lock_type>/n", argv[0]);
ACE_OS::exit(1);
}
//Polymorphic lock that will be used by the application
ACE_Lock *lock;
//Decide which lock you want to use at run time,
//recursive or non-recursive.
if(ACE_OS::strcmp(argv[3],"Recursive"))
lock=new ACE_Lock_Adapter<ACE_Recursive_Thread_Mutex>;
else
lock=new ACE_Lock_Adapter<ACE_Thread_Mutex>
//Setup the arguments
Args arg(lock,ACE_OS::atoi(argv[2]));
//spawn threads and wait as in previous examples..
}
在此例中,和前面的例子唯一的不同是
ACE_Lock 类是和 ACE_Lock_Adapter 一起使用的,以便能提供动态绑定。底层的锁定机制使用递归还是非递归互斥体,是在程序运行时由命令行参数决定的。使用动态绑定的好处是实际的锁定机制可以在运行时被替换。缺点是现在对锁的每次调用都需要负担额外的经由虚函数表的间接层次。
4.2.1.3
使用令牌( Token )如表
递归锁允许同一线程多次获取同一个锁。线程不会因为试图获取它已经拥有的锁而死锁。这些类型的锁能在各种不同 的情况下派上用场。例如,如果你用一个锁来维护跟踪流的一致性,你可能希望这个锁是递归的,因为某个方法可以调用一个跟踪例程,获取锁,被信号中断,然后 再尝试获取这个跟踪锁。如果锁是非递归的,线程将会在这里锁住它自己。你会发现很多其他需要递归锁的有趣应用。重要的是要记住,你获取递归锁多少次,就必须释放 它多少次。
在
尽管
4-1 中所提到的, ACE_Token 类提供所谓的“递归互斥体”,它可以被最初获得它的同一线程进行多次重新获取。 ACE_Token 类还确保所有试图获取它的线程按严格的 FIFO (先进先出)顺序排序。 SunOS 5.x 上运行例 4-3 ,释放锁的线程常常也是重新获得它的线程(大约 90% 的情况是这样)。但是如果你采用 ACE_Token 类作为锁定机制来运行这个例子,每个线程都会轮流获得令牌,然后有序地把机会让给下一个线程。 ACE_Token 作为所谓的递归锁非常有用,它们实际上是更大的“令牌管理”构架的一部分。该构架允许你维护数据存储中数据的一致性。遗憾的是,这已经超出了此教程的范围。
例
4-4#include "ace/Token.h"
#include "ace/Thread.h"
//Arguments that are to be passed to the worker thread are passed
//through this struct.
struct Args
{
public:
Args(int iterations):
token_(“myToken”),iterations_(iterations){}
ACE_Token token_;
int iterations_;
};
//The starting point for the worker threads
static void* worker(void*arguments)
{
Args *arg= (Args*) arguments;
for(int i=0;i<arg->iterations_;i++)
{
ACE_DEBUG((LM_DEBUG,"(%t) Trying to get a hold of this iteration/n"));
//This is our critical section
arg->token_.acquire();
ACE_DEBUG((LM_DEBUG,"(%t) This is iteration number %d/n",i));
//work
ACE_OS::sleep(2);
arg->token_.release();
}
return 0;
}
int main(int argc, char*argv[])
{
//same as previous examples..
}