Microsoft KB Archive/816160

= How to synchronize access to a shared resource in a multithreaded environment by using Visual C++ .NET or Visual C++ 2005 =

Article ID: 816160

Article Last Modified on 11/26/2007

-

APPLIES TO


 * Microsoft Visual C++ 2005 Express Edition
 * Microsoft Visual C++ .NET 2003 Standard Edition
 * Microsoft Visual C++ .NET 2002 Standard Edition

-







For a Microsoft Visual Basic .NET version of this article, see 316136.



For a Microsoft Visual C# .NET version of this article, see 816161.

This article refers to the following Microsoft .NET Class Library namespace:

System::Threading

IN THIS TASK

 * SUMMARY
 * Requirements
 * How to protect your global data in a multithreaded environment
 * How to make your class thread-safe
 * REFERENCES



SUMMARY
In applications that you create by using Microsoft Visual C++ .NET or Microsoft Visual C++ 2005, you can perform multiple tasks at the same time by using multithreading. Multithreading permits you to start different threads to complete different tasks at the same time. Multithreading also improves the performance and the responsiveness of your applications.

Because multiple threads can access a resource at the same time, it is best to synchronize individual threads with the other parts of your program. This article describes some common scenarios in multithreading programming. This article also explains how to synchronize access to a shared resource among multiple threads.

back to the top

Requirements
This article assumes that you are familiar with the following topic:

Visual C++ .NET or Visual C++ 2005 programming concepts

To implement the example that this article discusses, you must install the following software:

Visual C++ .NET or Visual C++ 2005

back to the top

How to protect your global data in a multithreaded environment
The public fields in your classes are accessible to all threads in your application. To synchronize access to these public fields, use properties instead of fields. Also, use a Mutex object to control access to these fields. To do this, follow these steps:  Start Microsoft Visual Studio .NET or Microsoft Visual Studio 2005. On the File menu, point to New, and then click Project. The New Project dialog box appears. Under Project Types, click Visual C++ Projects.

Note In Visual Studio 2005, click Visual C++ under Project Types. Do the following:  If you are using Visual C++ 2005, under Templates, click CLR Console Application. If you are using Visual C++ .NET 2003, under Templates, click Console Application (.NET).</li> If you are using Visual C++ .NET 2002, under Templates, click Managed C++ Application.</li></ul> </li> In the Name box, type ThreadApplication .</li> In the Location box, type C:\Test, and then click OK. By default, the ThreadApplication.cpp file is created.</li>  In the ThreadApplication.cpp file, replace the existing code in the _tmain function with the following code. // Set the number of threads. const int numOfThreads = 20;

// Create an instance of the callback class. // This class contains the callback function for all threads. CCallBack *thread = new CCallBack;

// Create an array of threads. Thread *threadArray[] = new Thread*[numOfThreads];

// Set the initial thread number. int threadNum =0;

// Create 20 threads. for (threadNum = 0;threadNum<numOfThreads;threadNum++) {  threadArray[threadNum] = new Thread(      new ThreadStart(NULL,CCallBack::AccessGlobalResource)); }

// Start the threads. for (threadNum = 0;threadNum<numOfThreads;threadNum++) {  threadArray[threadNum]->Start; }

// Wait until all the spawned threads finish. for (threadNum = 0;threadNum<numOfThreads;threadNum++) {  threadArray[threadNum]->Join; }

Console::WriteLine(&quot;All operations have completed. Press ENTER to quit.&quot;); Console::ReadLine; return 0; Notes

You must add the common language runtime support compiler option (/clr:oldSyntax) in Visual C++ 2005 to successfully compile the whole code sample. To add the common language runtime support compiler option in Visual C++ 2005, follow these steps: <ol style="list-style-type: lower-alpha;"> Click Project, and then click  Properties.

Note  is a placeholder for the name of the project.</li> Expand Configuration Properties, and then click General.</li> Click to select Common Language Runtime Support, Old Syntax (/clr:oldSyntax) in the Common Language Runtime support project setting in the right pane, click Apply, and then click OK.</li></ol>

For more information about the common language runtime support compiler option, visit the following Microsoft Web site:

/clr (Common Language Runtime Compilation)

http://msdn2.microsoft.com/en-us/library/k8d11d4s.aspx

The ThreadStart delegate represents the method that executes on the Thread class. When a thread is created, the new instance of the Thread class is created by using a constructor that takes the ThreadStart delegate as its only parameter.

The Thread.Start method causes the operating system to change the state of the current instance to ThreadState.Running.

The Thread.Join method blocks the calling thread until a thread terminates. </li> On the Project menu, click Add Class. The Add Class dialog box appears.</li> Under Templates, click Generic C++ Class, and then click Open. The Generic C++ Class Wizard - ThreadApplication dialog box appears.

Note In Visual C++ 2005, click C++ Class.</li> In the Class name box, type CCallBack, and then click Finish.</li> Open the CallBack.cpp file, and then delete the code for the constructor and the code for the destructor.</li>  Open the CallBack.h file, and then replace the existing code with the following code.
 * 1) pragma once
 * 2) include &quot;stdafx.h&quot;
 * 3) include &quot;SharedClass.h&quot;

public __gc class CCallBack { private: // Create a static instance of the class that contains the shared resource. static SharedClass *sharedResource = new SharedClass;

public : // Define the callback function for threads. // Static member functions can be accessed without using an instance of the corresponding class type. static void AccessGlobalResource {       // Use an instance of the Random class to generate random numbers. Random *rnd = new Random; long theNumber;

// When the random number MOD 2 is zero, get the number. // Otherwise set the number. // Use the following logic to switch the calls to get and to set the random number: if ((rnd->Next%2) != 0 ) {           theNumber = sharedResource->Number; }       else {           theNumber = (int)rnd->Next; sharedResource->Number = theNumber; }      } }; </li>  Open the ThreadApplication.cpp file, and then add the following code at the top of the code window: On the Project menu, click Add Class. The Add Class dialog box appears.</li> Under Templates, click Generic C++ Class, and then click Open. The Generic C++ Class Wizard - ThreadApplication dialog box appears.
 * 1) include &quot;CallBack.h&quot; </li>

Note In Visual Studio 2005, click C++ Class.</li> <li>In the Class name box, type CSharedClass, and then click Finish.</li> <li>Open the SharedClass.cpp file, and then delete the code for the constructor and the code for the destructor.</li> <li> Open the SharedClass.h file, and then replace the existing code with the following code.
 * 1) pragma once
 * 2) include &quot;stdafx.h&quot;
 * 3) using <mscorlib.dll>
 * 4) include <tchar.h>

using namespace System; using namespace System::Threading;

public __gc class SharedClass { private: // Declare a pointer to the lock that is used for reading and writing. ReaderWriterLock *rwl;

// All threads access the following shared resource: long myNumber;

public: SharedClass {       rwl = new ReaderWriterLock; }

public: // Use the following property method to get the number. __property long get_Number {       // Acquire a read lock on the resource. rwl->AcquireReaderLock(Timeout::Infinite); try {           Console::WriteLine(&quot;Thread:{0} starts to read the number&quot;,               Thread::CurrentThread->GetHashCode.ToString);

Thread::Sleep(50);

Console::WriteLine(&quot;Thread:{0} has read the number&quot;,              Thread::CurrentThread->GetHashCode.ToString); return myNumber; }       __finally {           // Release the read lock. rwl->ReleaseReaderLock; }                   }

// Use the following property method to set the number. __property void set_Number(long Value) {       // Acquire a write lock on the resource. rwl->AcquireWriterLock(Timeout::Infinite); try {           Console::WriteLine(&quot;Thread: {0} starts to write the number&quot;,               Thread::CurrentThread->GetHashCode.ToString);

Thread::Sleep(50); myNumber = Value;

Console::WriteLine(&quot;Thread: {0} has written the number&quot;,              Thread::CurrentThread->GetHashCode.ToString); }       __finally {           // Release the write lock. rwl->ReleaseWriterLock; }   } }; </li> <li>Press CTRL+F5 to build and then run your application.</li></ol>

back to the top

How to make your class thread-safe
Multiple threads may try to access an object at the same time. When more than one thread tries to access an object at the same time, some threads may retrieve an invalid state of the object if one thread modifies the object while the threads are trying to access it.

For example, if a thread is reading a field of the object while another thread is modifying the field, the first thread may retrieve an invalid state of the field. This situation is known as a race condition.

To avoid this situation, use synchronization objects to protect critical sections of your code from race conditions. A Mutex object is one type of synchronization object. A Mutex object permits a single thread to obtain exclusive modification rights on an object. To use synchronization, follow these steps: <ol> <li>Start Visual Studio .NET or Visual Studio 2005.</li> <li>On the File menu, point to New, and then click Project. The New Project dialog box appears.</li> <li>Under Project Types, click Visual C++ Projects.

Note In Visual Studio 2005, click Visual C++ under Project Types.</li> <li>Do the following: <ul> <li>If you are using Visual C++ 2005, under Templates, click CLR Console Application.</li> <li>If you are using Visual C++ .NET 2003, under Templates, click Console Application (.NET).</li> <li>If you are using Visual C++ .NET 2002, under Templates, click Managed C++ Application.</li></ul> </li> <li>In the Name box, type SyncThread .</li> <li>In the Location box, type C:\Test, and then click OK. By default, the SyncThread.cpp file is created.</li> <li> In the SyncThread.cpp file, replace the existing code in the _tmain function with the following code. CCallBack *callbackClass = new CCallBack; return 0; </li> <li> In the SyncThread.cpp file, locate the following code. <li> Add the following code after the line that you located in step 8. using namespace System; using namespace System::Threading; </li> <li>On the Project menu, click Add Class. The Add Class dialog box appears.</li> <li>Under Templates, click Generic C++ Class, and then click Open. The Generic C++ Class Wizard - SyncThread dialog box appears.
 * 1) include &quot;stdafx.h&quot; </li>
 * 1) include &quot;CallBack.h&quot;

Note In Visual Studio 2005, click C++ Class.</li> <li>In the Class name box, type CCallBack, and then click Finish.</li> <li>Open the CallBack.cpp file, and then delete the code for the constructor and the code for the destructor.</li> <li> Open the CallBack.h file, and then replace the existing code with the following code.
 * 1) pragma once
 * 2) include &quot;Student.h&quot;

using namespace System; __gc class CCallBack { private: static int WorkItemNum; static AutoResetEvent *Done;
 * 1) include <tchar.h>

public: CCallBack {       WorkItemNum = 20; Done = new AutoResetEvent(false); int threadNum=0; Student *AStudent = new Student;

// Queue 20 work-items in the thread pool. for (threadNum = 0;threadNum<WorkItemNum; threadNum++) {           ThreadPool::QueueUserWorkItem(new WaitCallback(NULL,AccessClassResource), AStudent); }

Done->WaitOne;

Console::WriteLine(&quot;All operations have completed. Press ENTER to quit.&quot;); Console::ReadLine;

}

static void AccessClassResource(Object *state) {       Random *rnd = new Random; String *theName; Student *AStudent = static_cast<Student*>( state);

if (rnd->Next % 2 != 0) {           // Perform some operation on the TeacherName static member. if (rnd->Next % 2 != 0) {               // Write to the TeacherName member. switch(rnd->Next % 3) {                   case 0: AStudent->TeacherName = &quot;Tom&quot;; case 1: AStudent->TeacherName = &quot;Mike&quot;; case 2: AStudent->TeacherName = &quot;John&quot;; }           }            else {               theName = AStudent->TeacherName; }       }        else {           // Perform some operation on the instance member. if (rnd->Next % 2 != 0) {               // Write to the instance member. switch (rnd->Next % 3) {                   case 0: AStudent->SetName(&quot;Janet&quot;); case 1: AStudent->SetName(&quot;David&quot;); case 2: AStudent->SetName(&quot;Ben&quot;); }           }            else {               // Read the instance member. theName = AStudent->GetName; }       }

// Because multiple threads may access the WorkItemNum value, // use the Interlocked::Decrement method to decrease its value. if (Interlocked::Decrement(&WorkItemNum) == 0) {           // Set the event to notify the main thread that all work has completed. Done->Set; }   } }; </li> <li>On the Project menu, click Add Class. The Add Class dialog box appears.</li> <li>Under Templates, click Generic C++ Class, and then click Open. The Generic C++ Class Wizard - SyncThread dialog box appears.

Note In Visual Studio 2005, click C++ Class.</li> <li>In the Class name box, type Student, and then click Finish.</li> <li>Open the Student.cpp file, and then delete the code for the constructor and the code for the destructor.</li> <li> Open the Student.h file, and then replace the existing code with the following code. using namespace System; using namespace System::Threading;
 * 1) pragma once

__gc class Student { private: String *myTeacherName; String *myName; Mutex *mut; public: Student {       myTeacherName = new String(&quot;Bill&quot;); myName = new String(&quot;Grace&quot;); mut = new Mutex; }   __property String* get_TeacherName {       mut->WaitOne; String *theName; Console::WriteLine(&quot;Thread {0} starts to read the teacher's name&quot;,              Thread::CurrentThread->GetHashCode.ToString); theName = myTeacherName;

// Wait 0.3 seconds. Thread::Sleep(300); Console::WriteLine(&quot;Thread {0} has read the teacher's name:{1}.&quot;,              Thread::CurrentThread->GetHashCode.ToString, theName); mut->ReleaseMutex;

return theName; }

__property void set_TeacherName(String *Value) {       mut->WaitOne; Console::WriteLine(&quot;Thread {0} starts to write the teacher's name.&quot;,              Thread::CurrentThread->GetHashCode.ToString); myTeacherName = Value;

// Wait 0.3 seconds. Thread::Sleep(300); Console::WriteLine(&quot;Thread {0} has written the teacher's name:{1}.&quot;,              Thread::CurrentThread->GetHashCode.ToString, Value); mut->ReleaseMutex; }

String *GetName {       mut->WaitOne; String *theName;

Console::WriteLine(&quot;Thread {0} starts to read the student's name.&quot;,              Thread::CurrentThread->GetHashCode.ToString); theName = myName; // Wait 0.3 seconds. Thread::Sleep(300); Console::WriteLine(&quot;Thread {0} has read the student's name:{1}&quot;,              Thread::CurrentThread->GetHashCode.ToString, theName); mut->ReleaseMutex; return theName; }

String* SetName(String *NewName) {       mut->WaitOne; String *theOldName;

Console::WriteLine(&quot;Thread {0} starts to write the student's name.&quot;,              Thread::CurrentThread->GetHashCode.ToString); theOldName = myName; myName = NewName; // Wait 0.3 seconds. Thread::Sleep(300); Console::WriteLine(&quot;Thread {0} has written the student's name:{1}&quot;,              Thread::CurrentThread->GetHashCode.ToString, NewName); mut->ReleaseMutex; return theOldName; } }; </li> <li>Press CTRL+F5 to build and then run the application.</li></ol>

back to the top

<div class="references_section">