How to synchronize access to a shared resource in a multithreaded environment by using Visual C++ .NET or Visual C++ 2005 (816160)
The information in this article applies to:
- Microsoft Visual C++ 2005 Express Edition
- Microsoft Visual C++ .NET (2003)
- Microsoft Visual C++ .NET (2002)
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: IN THIS TASKSUMMARYIn 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
topRequirementsThis 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 topHow to protect your global data in a multithreaded
environmentThe 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).
- If you are using Visual C++ .NET 2002, under
Templates, click Managed C++
Application.
- In the Name box, type
ThreadApplication.
- In the Location box, type
C:\Test, and then click OK. By default, the
ThreadApplication.cpp file is created.
- 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("All operations have completed. Press ENTER to quit.");
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:
- Click Project, and then click <ProjectName> Properties.
Note <ProjectName> is a placeholder for the name of the project. - Expand Configuration Properties, and then click General.
- 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.
For more information about the common language runtime support compiler option, visit the following Microsoft Web site: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. - On the Project menu, click Add
Class. The Add Class dialog box appears.
- 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. - In the Class name box, type
CCallBack, and then click
Finish.
- Open the CallBack.cpp file, and then delete the code for
the constructor and the code for the destructor.
- Open the CallBack.h file, and then replace the existing
code with the following code.
#pragma once
#include "stdafx.h"
#include "SharedClass.h"
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;
}
}
}; - Open the ThreadApplication.cpp file, and then add the
following code at the top of the code window:
#include "CallBack.h" - On the Project menu, click Add
Class. The Add Class dialog box appears.
- Under Templates, click Generic C++
Class, and then click Open. The Generic C++
Class Wizard - ThreadApplication dialog box appears.
Note In Visual Studio 2005, click C++ Class. - In the Class name box, type
CSharedClass, and then click
Finish.
- Open the SharedClass.cpp file, and then delete the code
for the constructor and the code for the destructor.
- Open the SharedClass.h file, and then replace the existing
code with the following code.
#pragma once
#include "stdafx.h"
#using <mscorlib.dll>
#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("Thread:{0} starts to read the number",
Thread::CurrentThread->GetHashCode().ToString());
Thread::Sleep(50);
Console::WriteLine("Thread:{0} has read the number",
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("Thread: {0} starts to write the number",
Thread::CurrentThread->GetHashCode().ToString());
Thread::Sleep(50);
myNumber = Value;
Console::WriteLine("Thread: {0} has written the number",
Thread::CurrentThread->GetHashCode().ToString());
}
__finally
{
// Release the write lock.
rwl->ReleaseWriterLock();
}
}
}; - Press CTRL+F5 to build and then run your
application.
back to the topHow to make your class
thread-safeMultiple 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:
- Start Visual Studio .NET or 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).
- If you are using Visual C++ .NET 2002, under
Templates, click Managed C++
Application.
- In the Name box, type
SyncThread.
- In the Location box, type
C:\Test, and then click OK. By default, the
SyncThread.cpp file is created.
- In the SyncThread.cpp file, replace the existing code in
the _tmain function with the following code.
CCallBack *callbackClass = new CCallBack();
return 0; - In the SyncThread.cpp file, locate the following code.
#include "stdafx.h" - Add the following code after the line that you located in
step 8.
#include "CallBack.h"
using namespace System;
using namespace System::Threading; - On the Project menu, click Add
Class. The Add Class dialog box appears.
- 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. - In the Class name box, type
CCallBack, and then click
Finish.
- Open the CallBack.cpp file, and then delete the code for
the constructor and the code for the destructor.
- Open the CallBack.h file, and then replace the existing
code with the following code.
#pragma once
#include "Student.h"
using namespace System;
#include <tchar.h>
__gc class CCallBack
{
private:
static int WorkItemNum;
static AutoResetEvent *Done;
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("All operations have completed. Press ENTER to quit.");
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 = "Tom";
case 1:
AStudent->TeacherName = "Mike";
case 2:
AStudent->TeacherName = "John";
}
}
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("Janet");
case 1:
AStudent->SetName("David");
case 2:
AStudent->SetName("Ben");
}
}
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();
}
}
}; - On the Project menu, click Add
Class. The Add Class dialog box appears.
- 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. - In the Class name box, type
Student, and then click
Finish.
- Open the Student.cpp file, and then delete the code for
the constructor and the code for the destructor.
- Open the Student.h file, and then replace the existing code
with the following code.
#pragma once
using namespace System;
using namespace System::Threading;
__gc class Student
{
private:
String *myTeacherName;
String *myName;
Mutex *mut;
public:
Student()
{
myTeacherName = new String("Bill");
myName = new String("Grace");
mut = new Mutex();
}
__property String* get_TeacherName()
{
mut->WaitOne();
String *theName;
Console::WriteLine("Thread {0} starts to read the teacher's name",
Thread::CurrentThread->GetHashCode().ToString());
theName = myTeacherName;
// Wait 0.3 seconds.
Thread::Sleep(300);
Console::WriteLine("Thread {0} has read the teacher's name:{1}.",
Thread::CurrentThread->GetHashCode().ToString(), theName);
mut->ReleaseMutex();
return theName;
}
__property void set_TeacherName(String *Value)
{
mut->WaitOne();
Console::WriteLine("Thread {0} starts to write the teacher's name.",
Thread::CurrentThread->GetHashCode().ToString());
myTeacherName = Value;
// Wait 0.3 seconds.
Thread::Sleep(300);
Console::WriteLine("Thread {0} has written the teacher's name:{1}.",
Thread::CurrentThread->GetHashCode().ToString(), Value);
mut->ReleaseMutex();
}
String *GetName()
{
mut->WaitOne();
String *theName;
Console::WriteLine("Thread {0} starts to read the student's name.",
Thread::CurrentThread->GetHashCode().ToString());
theName = myName;
// Wait 0.3 seconds.
Thread::Sleep(300);
Console::WriteLine("Thread {0} has read the student's name:{1}",
Thread::CurrentThread->GetHashCode().ToString(), theName);
mut->ReleaseMutex();
return theName;
}
String* SetName(String *NewName)
{
mut->WaitOne();
String *theOldName;
Console::WriteLine("Thread {0} starts to write the student's name.",
Thread::CurrentThread->GetHashCode().ToString());
theOldName = myName;
myName = NewName;
// Wait 0.3 seconds.
Thread::Sleep(300);
Console::WriteLine("Thread {0} has written the student's name:{1}",
Thread::CurrentThread->GetHashCode().ToString(), NewName);
mut->ReleaseMutex();
return theOldName;
}
}; - Press CTRL+F5 to build and then run the
application.
back to the
topREFERENCESFor more information, visit the following Microsoft
Developer Network (MSDN) Web sites: back to the
top
| Modification Type: | Major | Last Reviewed: | 12/30/2005 |
|---|
| Keywords: | kbThreadSync kbThread kbHOWTOmaster KB816160 kbAudDeveloper |
|---|
|
|
|
©2004 Microsoft Corporation. All rights reserved.
|
|