ACE線程安全與同步(Thread Safety and Synchronization)

保護原語
爲了確保一致性,多線程程序必須在共享數據周圍使用保護原語。
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. };  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章