Chapter: Multicore Application Programming For Windows, Linux, and Oracle Solaris - Windows Threading

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail

Protecting Access to Code with Critical Sections

Critical sections are one method of ensuring only a single thread executes a region of code.

Protecting Access to Code with Critical Sections

 

Critical sections are one method of ensuring only a single thread executes a region of code. They are declared within a process and are not resources provided by the kernel (they have no handles). Since they are entirely within the process, access to them is quicker than it would be if access had to be brokered by the kernel.

 

The code in Listing 6.14 declares a critical section structure, initializes it with a call to InitializeCriticalSection(), and, once the program has finished with it, deletes it with a call to DeleteCriticalSection().

 

Listing 6.14   Using Initialization and Deletion of Critical Sections

CRITICAL_SECTION critical;

 

...

 

InitializeCriticalSection(&critical);

 

 

DeleteCriticalSection(&critical);

 

...

When a thread wants to enter the critical section, it calls EnterCriticalSection(). If no other thread is in the critical section, the calling thread acquires it and continues execution. If another thread is in the critical section, the calling thread will sleep until the thread executing the critical section leaves it with the call LeaveCriticalSection(). The thread that calls EnterCriticalSection() will not return until it has obtained access to the critical section; there is no concept of a timeout.

 

Listing 6.15 shows an example of using a critical section to protect access to the vari-able counter.

 

Listing 6.15   Using a Critical Section to Protect Access to a Variable

volatile int counter = 0;

 

CRITICAL_SECTION critical;

 

unsigned int __stdcall test( void * )

 

{

 

while ( counter<100 )

 

{

 

EnterCriticalSection( &critical );

 

int number = counter++;

 

LeaveCriticalSection( &critical );

 

printf( "ThreadID %i; value = %i, is prime = %i\n", GetCurrentThreadId(), number, isprime(number) );

}

 

return 0;

 

}

 

int _tmain( int argc, _TCHAR* argv[] )

 

{

 

HANDLE h1, h2;

 

InitializeCriticalSection( &critical );

 

h1 = (HANDLE)_beginthreadex( 0, 0, &test, (void*)0, 0, 0); h2 = (HANDLE)_beginthreadex( 0, 0, &test, (void*)1, 0, 0); WaitForSingleObject( h1, INFINITE );

WaitForSingleObject( h2, INFINITE );

 

CloseHandle( h1 );

 

CloseHandle( h2 ); getchar();

DeleteCriticalSection( &critical );

 

return 0;

 

}

 

Putting threads to sleep and waking them up again is time-consuming, because it involves entering the kernel. All critical sections should be designed to be as short-lived as possible. With that in mind, it is likely that by the time the thread has been put to sleep, the thread that was in the critical section will already have left it. Therefore, mak-ing the waiting thread sleep and then waking it up again is just a waste of time.

 

There are two alternatives. The programmer can call TryEnterCriticalSection(), which will return immediately returning either true, meaning that the thread has acquired access to the critical section, or false, meaning that another thread is currently in the critical section. The code that protects access to the counter variable could be written using TryEnterCriticalSection(), as shown in Listing 6.16.

 

Listing 6.16 Using TryEnterCriticalSection() to Avoid Putting Calling Threads to Sleep

while (counter<100)

 

{

 

while ( !TryEnterCriticalSection( &critical ) ) {} int number = counter++;

LeaveCriticalSection( &critical );

 

printf( "ThreadID %i; value = %i, is prime = %i\n",

 

GetCurrentThreadId(), number, isprime(number) );

}

 

 

This would cause the process to spin continuously until it got the lock. One of the problems with having a thread spin is that it is potentially depriving other threads of processor time. Of particular concern would be the case where the spinning thread stops the other thread, which is currently in the critical section, from getting back onto the processor. Consequently, this style of programming is one that should only be under-taken with care.

 

The other approach is to have the thread wanting to enter the critical section spin briefly in the hope that the thread currently in the critical section will soon leave. If the other thread leaves the critical section, the spinning thread can immediately enter the critical section. Once the thread has spun for a predetermined count, the thread goes to sleep until the other thread eventually leaves the critical section. This approach represents a trade-off between the immediacy of spinning for access to the critical section and the poor utilization of resources that spinning causes.

 

Critical sections support this idea of spinning for a short time before sleeping. There are two ways of setting the number of times that a thread calling EnterCriticalSection() will spin before it goes to sleep. The critical section can be initialized with the value through the initialization call

 

InitializeCriticalSectionAndSpinCount(), which takes the pointer to the critical section, and the spin count as parameters. Or, once the critical section has been created, the spin count can be set through a call to SetCriticalSectionSpinCount(). Listing 6.17 shows calls to these routines.

 

 

Listing 6.17   Methods of Setting the Spin Count for a Critical Section

InitializeCriticalSectionAndSpinCount( &critical, 1000 );

 

SetCriticalSectionSpinCount( &critical, 1000 );


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail


Copyright © 2018-2020 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.