Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SSW_8_11.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
5.22 Mб
Скачать

7.2 The Synchronization

Synchronizing resource access between threads is a common problem when writing multithreaded applications. Having two or more threads simultaneously access the same data can lead to undesirable and unpredictable results. For example, one thread could be updating the contents of a structure while another thread is reading the contents of the same structure. It is unknown what data the reading thread will receive: the old data, the newly written data, or possibly a mixture of both. MFC provides a number of synchronization and synchronization access classes to aid in solving this problem.

A typical multithreaded application has a class that represents a resource to be shared among threads. A properly designed, fully thread-safe class does not require you to call any synchronization functions. Everything is handled internally to the class, allowing you to concentrate on how to best use the class, not about how it might get corrupted. The best technique for creating a fully thread-safe class is to merge the synchronization class into the resource class. Merging the synchronization classes into the shared class is a straightforward process.

There are two states, which can be available for the task: 1) the task can be executed (or be ready for to execute); 2) the task can be blocked – it’s execution can be suspended until the needed resource will free or some event will happened.

The objects of synchronization

Win32 supports 4 types of synchronization objects. All of them are based on semaphore term:

  1. A "classic semaphore" — synchronization object that allows a limited number of threads in one or more processes to access a resource. On that selection, the access to the resource or completely limited (the only one thread or process has an access to the resource in some moment of time), or the limited value of threads and resources have such access.

The semaphore is implemented as a counter, which decreases when task receives the semaphore and increases, when the task frees it.

  1. The “exclusive semaphore” – mutex semaphore provides the complete access limitation to the resource. Such synchronization object allows one thread mutually exclusive access to a resource. Mutexes are useful when only one thread at a time can be allowed to modify data or some other controlled resource. For example, adding nodes to a linked list is a process that should only be allowed by one thread at a time.

  2. The "event" — a synchronization object that allows one thread to notify another that an event has occurred. It’s used to lock the access to the resource until some process will not declare, that the given resource can be used. Events are useful when a thread needs to know when to perform its task. For example, a thread that copies data to a data archive would need to be notified when new data is available. The Object signalize on the required event complete.

  3. The "critical section" — a synchronization object that allows one thread at a time to access a resource or section of code. Critical sections are useful when only one thread at a time can be allowed to modify data or some other controlled resource. For example, adding nodes to a linked list is a process that should only be allowed by one thread at a time.

The synchronization classes in MFC

The six multithreaded classes provided with MFC fall into two categories: synchronization objects (CSyncObject – the general class, which is the base class for the following: CSemaphore – the “classic” semaphore, CMutex – exclusive semaphore, CCriticalSection – the critical section, and CEvent – the event object) and synchronization access objects (CMultiLock and CSingleLock). The last two classes are supplementary. CMultiLock provides the access on two synchronization object, CSingleLock provides the access to one synchronization object.

Synchronization classes are used when access to a resource must be controlled to ensure integrity of the resource. Synchronization access classes are used to gain access to these controlled resources. To determine which synchronization class you should use, ask the following series of questions:

  1. Does the application have to wait for something to happen before it can access the resource (for example, data must be received from a communications port before it can be written to a file)? If yes, use CEvent.

  2. Can more than one thread within the same application access this resource at one time (for example, your application allows up to five windows with views on the same document)?

If yes, use CSemaphore.

  1. Can more than one application use this resource (for example, the resource is in a DLL)? If yes, use CMutex. If no, use CCriticalSection.

An object of class CSingleLock represents the access-control mechanism used in controlling access to a resource in a multithreaded program. In order to use the synchronization classes CSemaphore, CMutex, CCriticalSection, and CEvent, you must create either a CSingleLock or CMultiLock object to wait on and release the synchronization object. Use CSingleLock when you only need to wait on one object at a time. Use CMultiLock when there are multiple objects that you could use at a particular time.

To use a CSingleLock object, call its constructor inside a member function in the controlled resource's class.

CSingleLock::CSingleLock ( CSyncObject* pObject, BOOL bInitialLock = FALSE );

Parameters:

pObject   Points to the synchronization object to be accessed. Cannot be NULL.

bInitialLock   Specifies whether to initially attempt to access the supplied object.

The access for the resource can be locked by Lock() function:

BOOL CSingleLock::Lock( DWORD dwTimeOut = INFINITE );

Parameters:

dwTimeOut   Specifies the amount of time to wait for the synchronization object to be available (signaled). If INFINITE, Lock will wait until the object is signaled before returning

Then call the IsLocked member function to determine if the resource is available. If it is, continue with the remainder of the member function. If the resource is unavailable, either wait for a specified amount of time for the resource to be released, or return failure. After use of the resource is complete, either call the Unlock function if the CSingleLock object is to be used again, or allow the CSingleLock object to be destroyed.

BOOL CSingleLock::Unlock( );

BOOL CSingleLock::Unlock( LONG lCount, LPLONG lPrevCount = NULL );

CSingleLock objects require the presence of an object derived from CSyncObject. This is usually a data member of the controlled resource's class.

The general way to control the resource access

  1. Create the object of CSyncObject (f.e. Semaphore) to control the resource access;

  2. Create the object of CSingleLock object with the synchronization object;

  3. Call the Lock() function to have the access to resource;

  4. Make the operation with resource;

  5. Call UnLock() function to free the semaphore.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]