|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionVC++ does not have an easy to use synchronized block like that of Java. But it is easier to simulate a macro which will work exactly like the synchronized block of Java. MFC provides classes for synchronization of threads. They are Who can read this articleDear reader, you should know how to create threads using MFC (yes using Why synchronizationIf two threads try to access the same data it may cause a fatal error. For example one thread may try to write into a file, while at the same time some other thread may try to read the data which was never written!! Only after the file is written, it should allow the other thread to read through some inter-thread communication. So to avoid the simultaneous access of data or parts of code, the threads should be protected by a mechanism, which allows only one thread to access the part of code or data. The data or code which is subject to multiple threads simultaneously is called a Critical Section. What this article tries to do?This article tries to simulate a synchronized block. The code or data inside the synchronized block ensures that only a single thread can access. The block also provides a way to wait till a flag is set by another thread which is termed as thread communication. Automatic release of the lock gained is ensured by the synchronized block using a technique called Resource Acquisition Is Initialization. The lock is released even if an exception is raised in the middle of the block. What this article does not try to explain?This article does not try to explain creation of threads. It just assumes you know how to create threads and face a problem in synchronization. It does not try to explain about deadlocks. How MFC provides synchronizationA MFC Advantages of using SYNCHRONIZED blockUsing MFC synchronization block we can synchronize code from corruption by multiple threads. But we may try to gain a lock and may not release the lock properly or we may try to access the critical sections of code without gaining a lock. The code which we may use to synchronize may not be legible. Using synchronized block is legible. Further it ensures that the lock is released properly even under abnormal exceptions using Resource Acquisition Is Initialization. The real advantage of synchronized block is it makes thread communication easier by using a function called Use of Resource Acquisition Is Initialization to release the Lock properlyLet us study the following example: //without using Resource Acquisition Is Initialization void some_func() { //allocate memory char *arr = new char[1024 * 5]; //5 kb is allocated //some code which may raise an exception //use the array //deallocate memory delete[] arr; } In the above sample code we saw how in case of an exception the memory will not be released properly which will result in memory leak. If there are no failures there will be no memory leaks. But there is a way by which the memory will get released in spite of exceptions. This can be achieved using a wrapper often called a smart pointer class which will take care of releasing the resource (memory, file, socket, handles ...) properly. See the following code: class CSmartCharArray { private: char *ptr; public: //store the starting address of the array in data member ptr CSmartCharArray(char *addr) : ptr(offset) {} //provide a conversion operator to char * for easy access operator char*() { return ptr; } //release the ptr when destructor is called virtual ~CSmartCharArray() { delete ptr; ptr = 0; //reset the ptr to point to null } }; ... ... ... //using Resource Acquisition Is Initialization void some_func() { //allocate memory CSmartCharArray arr(new char[1024 * 5]); //5 kb is allocated //some code which may raise an exception //use the array } In the above code the variable To release the Simulating SYNCHRONIZED//Synchronizer.h #ifndef SYNCHRONIZED_BLOCK_MFC #define SYNCHRONIZED_BLOCK_MFC #include <afxmt.h> #define SYNCHRONIZED(slock, cs) if(CSynchronizer slock = CSynchronizer(cs)) class CSynchronizer : public CSingleLock { public: CSynchronizer(CSyncObject *syncobj) : CSingleLock(syncobj, TRUE) {} operator BOOL() { return CSingleLock::IsLocked(); } BOOL Wait(DWORD dwTimeOut = INFINITE); BOOL Notify(); virtual ~CSynchronizer() {} }; #endif //Synchronizer.cpp #include "Synchronizer.h" ////////////////////////////////////////////////////////////////////// // Wait/Notify ////////////////////////////////////////////////////////////////////// BOOL CSynchronizer::Wait(DWORD dwTimeOut) { if(CSingleLock::IsLocked()) { CSingleLock::Unlock(); } return CSingleLock::Lock(dwTimeOut); } BOOL CSynchronizer::Notify() { if(CSingleLock::IsLocked()) { return CSingleLock::Unlock(); } return TRUE; } The SYNCHRONIZED macroNext we come to the most important thing in the code, the //we can declare a variable in the if condition as follows if(int i = 10) { //we can access variable i here if the if block is evaluated } else { //we can access variable i here if the else block is evaluated } //but i cannot be accessed here The variable declared in the For example: if(CSynchronizer syn = CSynchronizer(&some_critical_section)) { //do the processing on critical section of code here inside this if } //the variable syn cannot be used here since it's scope is over after if //and hence the lock gained is released automatically by destructor The above template code shows a simple synchronized block. The if(CSynchronizer syn = CSynchronizer(&some_critical_section)) { //do some processing //check if flag is set while(some_flag_is_not_set) { //release the lock temporarily to allow other thead to process //syn.Unlock(); //gain the lock again //syn.Lock(); //the above are two function calls and we may miss one call or //change the order (Unlock first and Lock next) so //call wait syn.Wait(); //which is the same as Unlock(); first and Lock(); second } //the lock gained is still there so //do the processing }//if (the lock is released automatically) The above #define SYNCHRONIZED(slock, cs) if(CSynchronizer slock = CSynchronizer(cs)) where the first parameter So the same wait template code inside a SYNCRHONIZED(syn, &some_critical_section)
{
//do delicate code on critical section or data
while(some_flag_is_not_set)
{
//wait till the flag is set
syn.Wait();
}//while
//do delicate processing
//call notify if you require explicit Unlocking
}//SYNCHRONIZED
The use of Enjoy easy synchronization
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||