ACE线程安全与同步(Thread Safety and Synchronization)

来源:互联网 发布:网络电视方案 编辑:程序博客网 时间:2024/05/18 02:14

保护原语
为了确保一致性,多线程程序必须在共享数据周围使用保护原语。
Protection Primitives in ACE

Primitive

Description

ACE_Mutex

Wrapper class around the mutual-exclusion mechanism and used to provide a simple, efficient mechanism to serialize access to a shared resource. Similar in functionality to a binary sempahore. Can be used for mutual exclusion among both threads and processes.

ACE_Thread_Mutex

Can be used in place of ACE_Mutex and is specific for synchronization of threads.

ACE_Recursive_Thread_Mutex

Mutex that is recursive, that is, can be acquired multiple times by the same thread. Note that to work correctly, it must be released as many times as it was acquired.

ACE_RW_Mutex

Wrapper class that encapsulates readers/writer locks. These locks are acquired differently for reading and writing, thus enabling multiple readers to read while no one is writing. These locks are nonrecursive.

ACE_RW_Thread_Mutex

Can be used in place of ACE_RW_Mutex and is specific for synchronization of threads.

ACE_Token

Tokens are the richest and heaviest locking primitive available in ACE. The locks are recursive and allow for read and write locking. Also, strict FIFO ordering of acquisition is enforced; all threads that call acquire() enter a FIFO queue and acquire the lock in order.

ACE_Atomic_Op

Template wrapper that allows for safe arithmetic operations on the specified type.

ACE_Guard

Template-based guard class that takes the lock type as a template parameter.

ACE_Read_Guard

Guard that uses acquire_read() on a read/write guard during construction.

ACE_Write_Guard

Guard that uses acquire_write() on a read/write guard during construction.

递归互斥体(Recursive Mutexes)
如果某个线程两次获取同一个互斥体,线程会阻塞锁死自己(大多数情况)。
你可以使用递归互斥体来(ACE_Recursive_Thread_Mutex)避免这种情况,它允许同一个线程多次获取互斥体,而不会阻塞自己。
Readers/Writer 锁(Readers/Writer Locks)
Readers/Writer 锁(ACE_RW_Mutex)允许多线程同时持有一个锁进行读取,但只有一个线程能够持有这个锁进行写入。
只在读取竞争大大超过写入竞争时使用它,因为Readers/Writer 锁(ACE_RW_Mutex)都比互斥体慢。
原子运算包装(
Atomic Operation Wrapper)
如果机器内存是强序(strongly ordered)的,对一个处理器上的内存所做的修改就会立刻被其它处理器看到,就不需要对全局变量进行同步。
否则,你需要使用同步。
用一个
classic producer/consumer problem using busy waiting演示:
Cpp代码  收藏代码
  1. #include <ace/Atomic_Op_T.h>  
  2. #include <ace/Thread_Mutex.h>  
  3. #include <ace/Log_Msg.h>  
  4. #include <ace/Task.h>  
  5.   
  6. typedef ACE_Atomic_Op<ACE_Thread_Mutex, unsigned int> SafeUInt;  
  7. typedef ACE_Atomic_Op<ACE_Thread_Mutex, int> SafeInt;  
  8.   
  9. static const int Q_SIZE = 100;            //缓冲区大小  
  10. static const int MAX_PROD = 1000;        //最大生产数  
  11.   
  12. class Consumer : public ACE_Task_Base  
  13. {  
  14. private:  
  15.     int* buf_;  
  16.     SafeUInt& in_;  
  17.     SafeUInt& out_;  
  18. public:  
  19.     Consumer(int *buf, SafeUInt &in, SafeUInt& out)  
  20.         : buf_(buf), in_(in), out_(out)  
  21.     { }  
  22.   
  23.     int svc(void)  
  24.     {  
  25.         while (1)  
  26.         {  
  27.             int item;  
  28.   
  29.             // Busy wait.  
  30.             do  
  31.             { }  
  32.             while (in_.value() - out_.value() == 0);  
  33.   
  34.             item = buf_[out_.value() % Q_SIZE];  
  35.             out_++;  
  36.   
  37.             ACE_DEBUG((LM_DEBUG,   
  38.                 ACE_TEXT("Consumed %d\n"),  
  39.                 item));  
  40.   
  41.             if (check_termination(item))  
  42.                 break;  
  43.         }  
  44.   
  45.         return 0;  
  46.     }  
  47.   
  48.     int check_termination(int item)  
  49.     {  
  50.         return (item == MAX_PROD);  
  51.     }  
  52. };  
  53.   
  54. class Producer : public ACE_Task_Base  
  55. {  
  56. private:  
  57.     int* buf_;  
  58.     SafeUInt& in_;  
  59.     SafeUInt& out_;  
  60. public:  
  61.     Producer(int* buf, SafeUInt& in, SafeUInt& out)  
  62.         : buf_(buf), in_(in), out_(out)  
  63.     { }  
  64.   
  65.     int svc(void)  
  66.     {  
  67.         SafeInt itemNo = 0;  
  68.         while (1)  
  69.         {  
  70.             // Busy wait.  
  71.             do  
  72.             { }  
  73.             while(in_.value() - out_.value() == Q_SIZE);  
  74.   
  75.             itemNo++;  
  76.             buf_[in_.value() % Q_SIZE] = itemNo.value();  
  77.             in_++;  
  78.   
  79.             ACE_DEBUG((LM_DEBUG,   
  80.                 ACE_TEXT("Produced %d \n"),  
  81.                 itemNo.value()));  
  82.   
  83.             if (check_termination(itemNo.value()))  
  84.                 break;  
  85.         }  
  86.   
  87.         return 0;  
  88.     }  
  89.   
  90.     int check_termination(int item)  
  91.     {  
  92.         return (item == MAX_PROD);  
  93.     }  
  94. };  
  95.   
  96. int ACE_TMAIN (int, ACE_TCHAR *[])  
  97. {  
  98.     int shared_buf[Q_SIZE];  
  99.     SafeUInt in = 0;  
  100.     SafeUInt out = 0;  
  101.   
  102.     Producer producer(shared_buf, in, out);  
  103.     Consumer consumer(shared_buf, in, out);  
  104.   
  105.     producer.activate();  
  106.     consumer.activate();  
  107.     producer.wait();  
  108.     consumer.wait();  
  109.   
  110.     return 0;  
  111. }  
 
令牌管理(Token Management)
是一个框架解决方案,有死锁检测特性。
线程同步(Thread Synchronization)
同步是这样一个过程,通过它你可以控制多个线程的执行次序,从而完成某项任务。
ACE Synchronization Primitives

Primitive

Description

ACE_Condition

A condition variable; allows signaling other threads to indicate event occurrence

ACE_Semaphore

A counting semaphore; can be used as a signaling mechanism and also for synchronization purposes

ACE_Barrier

Blocks all threads of execution until they all reach the barrier line, after which all threads continue

ACE_Event

A simple synchronization object that is used to signal events to other threads

使用信号量(Semaphores)
信号量是非负整型计数,用于协调对多个资源的访问。
获取信号量,计数就会减小;
释放信号量,计数就会增大;

计数到达0----不再有资源----试图获取该信号量的线程就会阻塞,直到信号量计数变得大于0位置。
把信号量计数初始化为某个非负值,表示你拥有的资源数量。
与互斥体的区别:
互斥体假定获取它的线程也将是释放它的线程;
信号量相反,一个线程获取由另一个线程释放。
////操作系统课程上的例题(生产者和消费者PV操作):
Cpp代码  收藏代码
  1. #include <ace/Log_Msg.h>  
  2. #include <ace/Task.h>  
  3. #include <ace/Semaphore.h>  
  4. #include <ace/Message_Block.h>  
  5.   
  6. class Consumer : public ACE_Task<ACE_MT_SYNCH>  
  7. {  
  8. private:  
  9.     ACE_Semaphore& psema_;  
  10.     ACE_Semaphore& csema_;  
  11.     int exit_condition_;  
  12.   
  13. public:  
  14.     enum { N_THREADS = 5 };  
  15.   
  16.     Consumer(ACE_Semaphore& psema, ACE_Semaphore& csema)  
  17.         : psema_(psema), csema_(csema), exit_condition_(0)  
  18.     { }  
  19.   
  20.     int svc(void)  
  21.     {  
  22.         while(!is_closed())  
  23.             consume_item();  
  24.         return 0;  
  25.     }  
  26.   
  27.     void consume_item()  
  28.     {  
  29.         csema_.acquire();  
  30.         if (!is_closed())  
  31.         {  
  32.             ACE_Message_Block *mb;  
  33.             this->getq(mb);  
  34.             if (mb->msg_type() == ACE_Message_Block::MB_HANGUP)  
  35.             {  
  36.                 //挂断消息  
  37.                 shutdown();  
  38.                 mb->release();  
  39.                 return;  
  40.             }  
  41.             else  
  42.             {  
  43.                 ACE_DEBUG((LM_DEBUG,  
  44.                     ACE_TEXT("(%t) Consumed %d\n"),  
  45.                     *((int*)mb->rd_ptr())));  
  46.                 mb->release();  
  47.             }  
  48.             //psema_值增一,允许生产者继续执行  
  49.             psema_.release();  
  50.         }  
  51.     }  
  52.   
  53.     void shutdown(void)  
  54.     {  
  55.         exit_condition_ = 1;  
  56.         //唤醒消息队列上的所有线程  
  57.         this->msg_queue()->deactivate();  
  58.         //释放在信号量上等待的所有线程  
  59.         csema_.release(N_THREADS);  
  60.     }  
  61.   
  62.     int is_closed(void)  
  63.     {  
  64.         return exit_condition_;  
  65.     }  
  66. };  
  67. class Producer : public ACE_Task_Base  
  68. {  
  69. public:  
  70.     enum { MAX_PROD = 128 };  
  71.   
  72.     Producer(ACE_Semaphore& psema, ACE_Semaphore& csema,  
  73.         Consumer &consumer)  
  74.         : psema_(psema), csema_(csema), consumer_(consumer)  
  75.     { }  
  76.   
  77.     int svc(void)  
  78.     {  
  79.         for (int i = 0; i <= MAX_PROD; i++)  
  80.             produce_item(i);  
  81.         hang_up();  
  82.         return 0;  
  83.     }  
  84.   
  85.     void produce_item(int item)  
  86.     {  
  87.         psema_.acquire();  
  88.         ACE_Message_Block *mb  
  89.             = new ACE_Message_Block(sizeof(int),  
  90.             ACE_Message_Block::MB_DATA);  
  91.         ACE_OS::memcpy(mb->wr_ptr(), &item, sizeof item);  
  92.         mb->wr_ptr(sizeof(int));  
  93.         this->consumer_.putq(mb);  
  94.   
  95.         ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%t) Produced %d\n"), item));  
  96.         //生产出一个新元素,csema_值增一  
  97.         csema_.release();  
  98.     }  
  99.   
  100.     void hang_up()  
  101.     {  
  102.         psema_.acquire();  
  103.         ACE_Message_Block *mb =  
  104.             new ACE_Message_Block(0, ACE_Message_Block::MB_HANGUP);  
  105.         this->consumer_.putq(mb);  
  106.         csema_.release();  
  107.     }  
  108.   
  109. private:  
  110.     ACE_Semaphore& psema_;  
  111.     ACE_Semaphore& csema_;  
  112.     Consumer& consumer_;  
  113. };  
  114. int ACE_TMAIN(int, ACE_TCHAR *[])  
  115. {  
  116.     ACE_Semaphore psem(5);        //队列中能容纳的最大数目为5  
  117.     ACE_Semaphore csem(0);        //在csem的release()之前,消费者完全不能进行消费  
  118.   
  119.     Consumer consumer(psem, csem);  
  120.     Producer producer(psem, csem, consumer);  
  121.   
  122.     producer.activate();  
  123.     //多个消费者线程  
  124.     consumer.activate(THR_NEW_LWP | THR_JOINABLE,  
  125.         Consumer::N_THREADS);  
  126.   
  127.     producer.wait();  
  128.     consumer.wait();  
  129.   
  130.     return 0;  
  131. }  
 
使用栅栏(Barriers)
每个线程在到达某种周知的状态时调用栅栏的wait(),阻塞起来,等待其它所有参与线程调用wait()表明它们也到达了该状态。
一旦所有线程都到达栅栏,它们就会被解除阻塞,并一起继续执行。
使用ACE_Barrier,当然你得传入同步线程的数目。
///让所有线程的启动和关闭时间同步
Cpp代码  收藏代码
  1. #include <ace/OS.h>  
  2. #include <ace/Log_Msg.h>  
  3. #include <ace/Task.h>  
  4. #include <ace/Barrier.h>  
  5.   
  6. class HA_CommandHandler : public ACE_Task<ACE_MT_SYNCH>  
  7. {  
  8. private:  
  9.     ACE_Barrier& startup_barrier_;  
  10.     ACE_Barrier& shutdown_barrier_;  
  11. public:  
  12.     enum { N_THREADS = 5 };  
  13.   
  14.     HA_CommandHandler(ACE_Barrier& startup_barrier, ACE_Barrier& shutdown_barrier)  
  15.         : startup_barrier_(startup_barrier),  
  16.         shutdown_barrier_(shutdown_barrier)  
  17.     { }  
  18.   
  19.     //随机初始化时间  
  20.     void initialize_handler(void)  
  21.     {  
  22.         ACE_OS::sleep(ACE_OS::rand() % 10);  
  23.     }  
  24.   
  25.     //随机执行时间  
  26.     int handle_command_requests(void)  
  27.     {  
  28.         ACE_OS::sleep(ACE_OS::rand() % 10);  
  29.         return -1;  
  30.     }  
  31.   
  32.     int svc(void)  
  33.     {  
  34.         initialize_handler();  
  35.         startup_barrier_.wait();  
  36.         ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%t: %D) Started\n")));  
  37.   
  38.         while(handle_command_requests() > 0)  
  39.             ;  
  40.   
  41.         shutdown_barrier_.wait();  
  42.         ACE_DEBUG((LM_DEBUG, ACE_TEXT("(%t: %D) Ended\n")));  
  43.   
  44.         return 0;  
  45.     }  
  46.   
  47. };  
  48. int ACE_TMAIN(int, ACE_TCHAR *[])  
  49. {  
  50.     ACE_Barrier startup_barrier(HA_CommandHandler::N_THREADS);  
  51.     ACE_Barrier shutdown_barrier(HA_CommandHandler::N_THREADS);  
  52.   
  53.     HA_CommandHandler handler(startup_barrier, shutdown_barrier);  
  54.     handler.activate(THR_NEW_LWP | THR_JOINABLE,  
  55.         HA_CommandHandler::N_THREADS);  
  56.     handler.wait();  
  57.     return 0;  
  58. }  
 
线程专有存储(Thread-Specific Storage)
当你创建线程时,你所创建的全部东西是:一个线程栈、一个信号掩码和一个任务控制块。
线程专有存储(
thread-specific storage ,TSS):能够存储专有于某个线程的状态信息。
使用
ACE_TSS模板类,你只需把你想要存储在TSS中的数据作为模板参数传递给它,然后用operator->()方法在你需要时对数据进行访问。operator->()会在首次调用时在TSS中创建并存储数据,ACE_TSS的析构器会确保TSS数据被适当移除和销毁。
Cpp代码  收藏代码
  1. class ClientContext  
  2. {  
  3. public:  
  4.     void *get_attribute(const char *name);  
  5.     void set_attribute(const char *name, void *value);  
  6.   
  7. private:  
  8.     Map attributeMap_;  
  9. };  
  10. class HA_CommandHandler : public ACE_Task<ACE_MT_SYNCH>  
  11. {  
  12. private:  
  13.     ACE_TSS<ClientContext> tss_ctx_;  
  14. public:  
  15.     virtual int svc(void)  
  16.     {  
  17.         //...  
  18.         this->tss_ctx_->get_attribute("attribute1");  
  19.   
  20.         return 0;  
  21.     }  
  22.   
  23. };