Synchronization Data Structures
The Concurrency Runtime provides several data structures that let you synchronize access to shared data from multiple threads. These data structures are useful when you have shared data that you modify infrequently. A synchronization object, for example, a critical section, causes other threads to wait until the shared resource is available. Therefore, if you use such an object to synchronize access to data that is used frequently, you can lose scalability in your application. The Parallel Patterns Library (PPL) provides the Concurrency::combinable class, which enables you to share a resource among several threads or tasks without the need for synchronization. For more information about the combinable class, see Parallel Containers and Objects.
The Concurrency::critical_section class represents a cooperative mutual exclusion object that yields to other tasks instead of preempting them. Critical sections are useful when multiple threads require exclusive read and write access to shared data.
The critical_section class is non-reentrant. The Concurrency::critical_section::lock method throws an exception of type Concurrency::improper_lock if it is called by the thread that already owns the lock.
Methods and Features
The following table shows the important methods that are defined by the critical_section class.
Acquires the critical section. The calling context blocks until it acquires the lock.
Tries to acquire the critical section, but does not block.
Releases the critical section.
The Concurrency::reader_writer_lock class provides thread-safe read/write operations to shared data. Use reader/writer locks when multiple threads require concurrent read access to a shared resource but rarely write to that shared resource. This class gives only one thread write access to an object at any time.
The reader_writer_lock class can perform better than the critical_section class because a critical_section object acquires exclusive access to a shared resource, which prevents concurrent read access.
Like the critical_section class, the reader_writer_lock class represents a cooperative mutual exclusion object that yields to other tasks instead of preempting them.
When a thread that must write to a shared resource acquires a reader/writer lock, other threads that also must access the resource are blocked until the writer releases the lock. The reader_writer_lock class is an example of a write-preference lock, which is a lock that unblocks waiting writers before it unblocks waiting readers.
Like the critical_section class, the reader_writer_lock class is non-reentrant. The Concurrency::reader_writer_lock::lock and Concurrency::reader_writer_lock::lock_read methods throw an exception of type improper_lock if they are called by a thread that already owns the lock.
Because the reader_writer_lock class is non-reentrant, you cannot upgrade a read-only lock to a reader/writer lock or downgrade a reader/writer lock to a read-only lock. Performing either of these operations produces unspecified behavior.
Methods and Features
The following table shows the important methods that are defined by the reader_writer_lock class.
Acquires read/write access to the lock.
Tries to acquire read/write access to the lock, but does not block.
Acquires read-only access to the lock.
Tries to acquire read-only access to the lock, but does not block.
Releases the lock.
The critical_section and reader_writer_lock classes provide nested helper classes that simplify the way you work with mutual exclusion objects. These helper classes are known as scoped locks.
The critical_section class contains the Concurrency::critical_section::scoped_lock class. The constructor acquires access to the provided critical_section object; the destructor releases access to that object. The reader_writer_lock class contains the Concurrency::reader_writer_lock::scoped_lock class, which resembles critical_section::scoped_lock, except that it manages write access to the provided reader_writer_lock object. The reader_writer_lock class also contains the Concurrency::reader_writer_lock::scoped_lock_read class. This class manages read access to the provided reader_writer_lock object.
Scoped locks provide several benefits when you are working with critical_section and reader_writer_lock objects manually. Typically, you allocate a scoped lock on the stack. A scoped lock releases access to its mutual exclusion object automatically when it is destroyed; therefore, you do not manually unlock the underlying object. This is useful when a function contains multiple return statements. Scoped locks can also help you write exception-safe code. When a throw statement causes the stack to unwind, the destructor for any active scoped lock is called, and therefore the mutual exclusion object is always correctly released.
When you use the critical_section::scoped_lock, reader_writer_lock::scoped_lock, and reader_writer_lock::scoped_lock_read classes, do not manually release access to the underlying mutual exclusion object. This can put the runtime in an invalid state.
The Concurrency::event class represents a synchronization object whose state can be signaled or non-signaled. Unlike synchronization objects, such as critical sections, whose purpose is to protect access to shared data, events synchronize flow of execution.
The event class is useful when one task has completed work for another task. For example, one task might signal another task that it has read data from a network connection or from a file.
Methods and Features
The following table shows several of the important methods that are defined by the event class.
For an example that shows how to use the event class, see Comparing Synchronization Data Structures to the Windows API.