Working with CRITICAL_SECTION

Environment: Visual Studio, All Windows platforms

-->

The Code

typedef class _MyCriticalSection
{
  long count;
  HANDLE Eventhandle;

public :
  _MyCriticalSection();
  void Enter();
  void Leave();
}MyCriticalSection;

That's it; everything is in the class!!!!!

The constructor creates an automatically resetting event and assigns it to EventHandle. Let us look at what goes on inside a general thread.

UINT func1(LPVOID lvoid)
{
  //WaitForSingleObject(handle,INFINITE);
  //This how you would use a CRITICAL_SECTION
Juhi.Enter();      //Simulates EnterCriticalSection(&cs);
  //EnterCriticalSection(&cs);
  for(intp=0;p<=4;p++)
    {
       cout<<"func1"<<endl;
    }
  AfxMessageBox("Func1");
  Juhi.Leave();    //Simulates LeaveCriticalSection(&cs);
                   //LeaveCriticalSection(&cs);
                   //This is how you would use a CRITICAL_SECTION
  return 1;
}

In Detail

Suppose there are 100 threads that call on enter, and say thread 77 is scheduled first, when enter executes, it will successfully validate the if condition namely if(InterlockedIncrement(&count)==1), (all the other threads will execute WaitForSingleObject(..) and wait infinitely) no matter when it is preempted by the OS, now this is the important point. One has to imagine the enter function getting preempted at all places and work out the logic for oneself.

So, other threads that call enter will fail the if(InterlockedIncrement(&count)==1) and block at that place (as they execute WaitForSingleObject(..)), until our thread 77 finishes the assigned job and calls the Leave() function. In the Leave() function, the count variable is decremented and Setevent(..) is called. From the remaining threads, one thread—say thread 45—gets to execute; in other words, its WaitForSingleObject(..) goes through. Because the event is automatically reset, none of the other threads go beyond the Enter() function. Simple, but amazing, no!!! Like that, all the rest of the threads execute one after the other.

One more point to be noted is that, although the kernel object event is used, if two threads do not clash, WaitForSingleObject(..) is never called and the thread never enters the Kernel mode so no time is wasted in entering the kernel mode and leaving it.

I have used three threads along with a MessageBox(..) inside each one of them, between Enter() and Leave(); until the First MessageBox is dismissed, no other thread will execute.

Well, this just the beginning. You can extend it across processeses. Imagine a critical section that works across process boundaries.

Downloads

Download demo project - 6 Kb


Comments

  • Nice idea, but needs work

    Posted by John M. Dlugosz on 10/27/2004 07:44pm

    The index states that this article explains the inner-workings of the CRITICAL_SECTION. But the article itself doesn't say what it's about, so I thought I must have missed the first page! It does illustrate a simple idea on how to avoid calling an OS locking object when there is no contention. But it's not how the Windows CRITICAL_SECTION does it. The latter uses a semaphore rather than an event, doesn't allocate it until it's first needed, and correctly handles multiple locks from the same thread, and is a much larger structure. You named your class "_MyCriticalSection". That is a reserved name! You should never use an identifier whose name begins with an underscore followed by a capital letter, since these are reserved for the implementation to use for any purpose. So it could step on a macro, or the compiler could assume it's something built in, or it might do something strange. Then you typedef it to "MyCriticalSection" and use that name everywhere except where you must use the real name (e.g. identifier in the declarator of the constructor). Why?? The "typedef class name1 {...} name2" construct serves no purpose. Just name it what you wanted to! Now the _RTL_CRITICAL_SECTION struct in the Windows header indeed uses a name beginning with _R, as it should since it is supplied by the compiler. It uses a typedef with a name losing the underscore, because this is a C header (as opposed to a C++ header). In C, the first identifier is a "struct tag" and not directly the name of a type. C doesn't have classes, so that's clearly not applicable here! The code is missing a destructor, so the HANDLE never gets closed when the object goes out of scope. The example code is problematic, too. I can sympathize that you want to show the simple Enter/Leave rather than another layer wrapping that, but the code shown is not something you should ever write in C++. So you need to be careful to explain that this is showing something underlying, and use something in the locked region that cannot throw an exception. See, when operator<< throws an exception (don't think "if", think "when"!) it will bypass the Leave and the lock will never be unlocked. The next time through it will deadlock. This class does not allow the same thread to enter the critical section again. If it tries, it will deadlock on itself. The CRITICAL_SECTION in Windows does handle this, and correctly requires the same number of Leaves to ballance the Enters. An article that explains the inner workings of CRITICAL_SECTION should do exactly that: =explain= why it's more efficient than a Win32 Mutex, and explain the concept of using a locked counter and avoiding the call to the OS unless it has to block anyway. Better yet, show how CRITICAL_SECTION actually works, rather than just the rough idea behind having it.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds