The topic area of concurrency patterns is poorly covered in literature, so this text is called to fill a gap, at least partly.
Implementation of Critical Resource Concurrency Pattern in C++
Many of those who have ever used Critical Section object for synchronization between threads were applying it together with Auto Critical Section object. Indeed, Auto Critical Section is a powerful synchronization object which use has a set of advantages. The code containing Auto Critical Section for Critical Section management turns safe, more readable and structured when compared with the code directly working with Critical Section. Moreover, Auto Critical Section has trivial implementation. Regarding Auto Critical Section object one might say that it wraps in Critical Section the scope in which it is being created.
However, whether Auto Critical Section used or not, there is still a series of problems relating to Critical Section object:
- the Critical Section and resource which it protects are being created separately and can potentially have different lifetime.
- the code which uses the resource must know that access to it is synchronized by critical section.
Both these problems are of little importance if the critical section is used for creation of a thread-safe class. In that case the critical section is being created as data member and about it should be aware only function members of the given class while creating an Auto Critical Section object on call. However, a problem appears if it is necessary to provide access to a resource or class object which doesn't make any attempts at synchronization from within.
Log system can serve as an example here. For logging purpose it is reasonable to create an object of
std::ofstream class and give global access to this object. Though, if single-threaded application using such tactics exists, then start of one more thread for logging purpose in this same application can lead to a number of problems:
- It will be necessary to create a critical section near object of
std::ofstreamclass and give a global access to it.
- It will be necessary to wrap up all places already using the object of
std::ofstreamclass in Auto Critical Section, and a problem here is that if any place appears not wrapped up such mistake will be hard to reveal.
The Critical Resource structural pattern serves for solving these problems.
The known synonyms are: Common Resource, Guardian Type.
Purpose: wraps not-synchronized object (unlike Auto Critical Section object which wraps scope) in critical section so that access to an object could be provided only through an entry in critical section, the leaving from which occurs right after end of access. So, Critical Resource pattern creates powerful alternative to using WinAPI Interlocked functions family.
Critical Resource object can be implemented by means of temporary objects via which access to a resource provided (by analogy with Auto Critical Section pattern which is implemented by means of automatic object).
In C++ it's the most naturally implementing Critical Resource as a template which accepts as a parameter the resource type and has as data members the resource itself and critical section, controlling access to it. Access to a resource will be made via temporary object of Resource Locker class which will be storing pointer to a resource and corresponding Critical Section. Critical Resource should have a function member returning temporary object of Resource Locker class which, by analogy with Auto Critical Section, will be entering into critical section at constructor and leave it at destructor. Resource Locker class as well should have a function member giving access to the reference to a resource. It should be noted that in C++ along with Resource Locker class, a Const Resource Locker class must be implemented. This class will provide access to a constant resource and objects of this class will be returned by constant function member of Critical Resource object.
Also it is important to take into account that sometimes it is required to synchronize not only an access to a resource, but also a series of such accesses. For this purpose
section() method in Critical Resource class should be implemented in order to return a reference to the critical section. Thus, if critical section is recursive (the critical section is recursive if one thread can enter it several times, at that in order some else thread could enter given critical section, the first one has to leave it as much times as it had entered. WinAPI
CRITICAL_SECTION synchronization object is recursive, but if implementation of critical section is performed on the basis of Auto Reset Event synchronization object, then in order to make such critical section recursive, additional actions should be undertaken), then there is no necessity to implement in the Critical Resource a function member providing uncontrollable access to a resource. Resource Locker will be entering into critical section recursively.
Also should be noticed that at Critical Resource' template it is convenient to make specialization for references. So it will be possible to create Critical Resource supervising access to already existing objects (for example,
At the diagram Critical Resource concurrency pattern can look as follows:
Implementation of Critical Resource concurrency pattern can look this way:
The main disadvantage of using Critical Resource lies in the fact that using of more than one Critical Resource in one expression is deadlock-unsafe, and also can lead to serious problems which will probably be hard to reveal. In order to avoid this kind of problems we can break the mentioned expression to several ones, each of which will be using one Critical Resource and copies of other resources at that. To resolve this problem copy() method was implemented; copy() method can be used in one expression as many times as it is necessary only if get() method wasn?t invoked in this expression.
Also it is necessary to pay attention to the fact that usage of TCriticalResource which limits access to STL containers (such as
std::list) for creation of a thread-safe container probably is not the best architectural decision. For this purpose it is better to use concurrency container patterns, such as: Buffer Monitor, Object Pool, Question Answer Pool, Blackboard.
Full implementation of TCriticalResource class can be taken from CriticalResource.zip.
Due to limited page width at this site we had to break lines in the examples of code, so we recommend you to download pdf version of the article.