Template and polymorphism strategy locking
Keywords: strategized mode template strategized polymorphism strategized locking mode ace boost C ++ Design Mode
There are a lot of strategized locking modes in ACE and boost implementations. This mode makes it easier for your class to be compatible with the locks and no locks. Ace master Douglas C. Schmidt has a special paper "strategized locking", which he has translated into "ace strategized locking mode".
The purpose of this article is to introduce the two implementation methods of this mode, in addition to the template parameters and polymorphism mode, and introduce the advantages and disadvantages of the two.
Policy-based locking of template parameters (parameterized)
If a monitoring component must be used in both multi-threaded and single-threaded environments, and its usage is very high, it must be locked under multiple threads, however, if this affects the performance of components in a single thread, it is not cost-effective. However, writing a set of code for each environment is obviously not conducive to code reuse. Is there a way to be compatible with the two modes? This is the strategy-based locking mode.
The Strategy-based locking method generally uses the template (ACE and boost have tried a similar method) to complete this function, resulting in the cost of the compiler, almost no performance impact. The following code:
// ZEN_Null_Mutex is a policy lock for a single thread. class ZEN_Null_Mutex {public: // locked. In fact, there is no void lock () {} // unlock, in fact, void unlock () {}} // ZEN_Thread_Mutex is a policy lock for multithreading. class ZEN_Thread_Mutex {protected: // thread lock pthread_mutex_t lock _; public: // lock. ZEN_ OS is a cross-platform encapsulated function. If this function is not expanded, you can think of it as mutex void lock () {ZEN_ OS: pthread_mutex_lock (& lock _);} // unlock, void unlock () {ZEN_ OS: pthread_mutex_unlock (& lock _) ;}// ZEN_Server_Statu S_T is a template to be used in multi-thread or single-thread scenarios. // The template parameter _ ZEN_LOCK determines whether the lock action is multithreading or single-thread template <class _ ZEN_LOCK> class ZEN_Server_Status_T {protected: // The strategized lock determines the behavior according to the template parameter _ ZEN_LOCK stat_lock _; public: // Add a monitoring value, to avoid conflict with void increase _ byidx (size_t index, int64_t inre) {stat_lock _. lock (); (stat_sandy_begin _ + index)-> counter _ + = incret; stat_lock _. unlock ();}......}
The above code template <class _ ZEN_LOCK> class ZEN_Server_Status_T is a monitoring class with the policy lock function. The template parameter _ ZEN_LOCK can be ZEN_Null_Mutex and ZEN_Thread_Mutex, in different environments, we can try different template parameters to use the ZEN_Server_Status_T monitoring component. In this way, everyone is happy,
In a multi-threaded environment, you can use the following to achieve synchronization security protection,
ZEN_Server_Status_T<ZEN_Thread_Mutex >::instance()->increase_byidx
In a single-threaded environment, you can achieve the fastest performance as follows.
ZEN_Server_Status_T<ZEN_NULL_Mutex >::instance()->increase_byidx
In this mode, you can easily change internal behaviors. For example, if you implement a read/write lock
The biggest benefit of this mode is that whether a class needs to lock the behavior determines the time to be put into the compilation period, which brings different behaviors to a piece of code, and has the lowest performance cost. For C ++ programmers, the strategy of using templates is a cool performance. It makes your code look more advanced (don't underestimate the influence of this factor ).
Policy-based templates
In fact, if you carefully read the essay "Strategized Locking" by Douglas C. Schmidt (unfortunately, you were not able to understand it when I was reading it), you will find such a paragraph.
There are two ways to strategize locking mechanisms: polymorphism and parameterized types. in general, parameterized types shoshould be used when the locking strategy is known at compile-time. likewise, polymorphism shocould be used when the locking strategy
Is not known until run-time. As usual, the trade-off is between the efficient run-time performance of parameterized types versus the potential for run-time extensibility with polymorphism.
There are two ways to implement the strategized lock mode: Polymorphism or template parameterization. Generally, template parameterization is used to determine the lock behavior during compilation. polymorphism is used to determine the lock policy at runtime. The factors that weigh the use of the template parameterization method or the polymorphism method are runtime performance and Runtime polymorphism scalability.
Recently, I have some new understandings of the above sentence in my own code. Due to the hierarchical relationship of the code, I must inherit the monitored class. Therefore, at the same time, because I want to ensure the lock effect, I still have to use the template lock policy.
// When inheriting the template mode, you must still use the template <class _ ZEN_LOCK> class Comm_Server_Status: public ZEN_Server_Status <_ ZEN_LOCK>.
This is not the most painful. Because there are many monitoring points, I have to add some monitoring operations to the Code in the basic library (note the library instead of the implementation. At this point, you will find that if you continue to retain the template lock policy, you must rewrite a lot of code.
// If the class ComponentA needs to use ZEN_Server_Status, // At the same time, Component_A is also a component and is also provided to others, you cannot decide whether to use any lock policy now. // you can only use the template's policy lock mode. The template <class _ ZEN_LOCK> class ComponentA {int fun_a1 {// If you need to use the monitoring data ZEN_Server_Status <_ ZEN_LOCK> in the center of the function fun_a1:: instance ()-> increase_byidx ...... } // If you want to use ZEN_Server_Status in component B, you must also use the policy lock template <class _ ZEN_LOCK> class ComponentA {}
If you want to use monitoring in many places, you must use a large amount of code as a template. Policy lock is also required. Although I do not think this is completely feasible, it is indeed suspected of enlarging the problem. Especially when your ComponentA may not need to consider thread synchronization requirements at all.
Observe the ACE code. Most of the ACE code is the template-based lock policy, but it does bring pain to a lot of ACE code (search for ACE_LOCK and you will know ), all ACE timers actually use the template-based policy lock mode. For the convenience of code, ACE is often used in the class where the thread lock is used as the template parameter, for example, the default ACE_Reactor is the implementation of locking, so in essence this does not bring better performance.
// In multi-threaded mode // ACE_Reactor // the actual result is // ACE_Select_Reactor_T <ACE_Reactor_Token_T <ACE_Token>: handle_events (ACE_Time_Value & maxwait_time)
Polymorphism strategy
So can the above problems be avoided to some extent by adopting polymorphism strategization? Yes. Polymorphism determines that the behavior is at runtime, rather than during compilation. Although there is a small amount of performance loss, it will decide the behavior time to move back, and it also gives the code more flexibility to some extent.
The following is a simple example of applying polymorphism to implement strategic locks.
// Implement the policy using polymorphism. zen_lock_base is used as a base class, and class zen_lock_base {public: // is locked. In fact, nothing is done, but it is also a virtual function, visual void lock () {}// unlock, but nothing is actually done, but it is also a virtual function, visual void unlock () {}} typedef zen_lock_base zen_null_mutex; // zen_thread_mutex is a policy lock for multithreading. Class zen_thread_mutex: Public zen_lock_base {protected: // thread lock pthread_mutex_t lock _; public: // locked, zen_ OS is a cross-platform encapsulated function. It is not expanded here. You can think of it as mutex visual void lock () {zen_ OS: PT Hread_mutex_lock (& lock _);} // unlock, visual void unlock () {zen_ OS: pthread_mutex_unlock (& lock _);}} // zen_server_status_p is a template to be used in multi-thread or single-thread scenarios. // The template parameter _ zen_lock determines whether the lock behavior is multi-thread or single-Thread class zen_server_status_p {protected: // The base class pointer of the polymorphism. The initialization parameter determines the behavior of the class zen_lock_base * stat_lock _; public: // initialization, determine the lock behavior based on external parameters void Init (bool multi_thread) {If (multi_thread) {stat_lock _ = zen_null_mutex ();} else {stat_lock _ = new ze N_thread_mutex () ;}// Add a metric value. If you are using multiple threads, avoid conflict with void increase _ byidx (size_t index, int64_t increx) {// The stat_lock _-> lock (); (stat_sandy_begin _ + index)-> counter _ + = incret; stat_lock _-> unlock ();} ......}
You will find that the lock policy can be determined during function initialization, and you do not need to extend the influence of template parameters to various places. For example, you can directly use zen_server_status_p in a large number of class libraries, because at this time you do not need to decide his specific behavior. Of course, because the dynamic operation determines the behavior, the virtual function still has a little performance overhead. Of course, you don't need to think too much about this issue under the current CPU computing power.
Comparison of Two strategic approaches
Template strategization uses the template parameters to implement strategization, and puts the behavior of the policy into the compilation period to achieve optimal performance. But it is suitable for small-sized codes, or it is used by the template Code itself (pursuing cool code ). At the same time, because the policy decision time is put in the compilation phase, it will inherit and must also use template policy behavior for large-scale use. This expands the impact of policies.
Polymorphism is to use the polymorphism method to determine the policy behavior. At runtime, the caller determines what polymorphism behavior is used through parameters. At the same time, because the behavior is decided to be placed at runtime, it can be used without much changes to the relevant code. The disadvantage is that the performance is a little weaker than the template-based strategizing. in operation, the caller must control the policy behavior.
In comparison, I think there are better application scenarios for polymorphism and strategization. In my personal sense, sometimes excessive template design will reduce code availability and affect users.
[The author of this article is Yan du Han tan, in the spirit of freedom, you can be in the profit of the complete reprinted this document, reprinted Please attach blog link: http://www.cnblogs.com/fullsail/ or http://blog.csdn.net/fullsail, the negative is a dollar, every figure one hundred no. Double the Baidu Library price]