Chapter7-Synchronization Examples
Classic Problems of Synchronization
The Bounded-Buffer Problem
In our problem, the producer and consumer processes share the following data structures:
1 | int n; |
The structure of the producer process.
1 | while (true) { |
The structure of the consumer process.
1 | while (true) { |
The Readers–Writers Problem
readers and writers are not symmetric (read操作可以同时进行,但是write不行)
Obviously, if two readers access the shared data simultaneously, no adverse effects will result. However, if a writer and some other process (either a reader or a writer) access the database simultaneously, chaos may ensue.
In the solution to the first readers–writers problem, the reader processes share the following data structures:
1 | semaphore rw_mutex = 1; // mutex for read and write |
The structure of a writer process.
1 | while (true) { |
The structure of a reader process.
1 | while (true) { |
另一种处理readers—writers problem的方法:read copy update(RCU)
把数据复制多份,就不会产生冲突
The Dining-Philosophers Problem
Semaphore Solution
One simple solution is to represent each chopstick with a semaphore. semaphore chopstick[5]; are set as shared data, where all the elements of chopstick are initialized to 1.
The structure of philosopher i:
1 | while (true) { |
The solution could create a deadlock. When each philosopher tries to grab her right chopstick, she will be delayed forever.
Monitor Solution
This solution imposes the restriction that a philosopher may pick up her chopsticks only if both of them are available. To code this solution, we need to distinguish among three states: enum {THINKING, HUNGRY, EATING} state[5];
We also need to declare condition self[5]. This allows philosopher i to delay herself when she is hungry but is unable to obtain the chopsticks she needs.
The distribution of the chopsticks is controlled by the monitor DiningPhilosophers:
1 | monitor DiningPhilosophers |
Each philosopher, before starting to eat, must invoke the operation pickup(). This act may result in the suspension of the philosopher process. The whole process:
1 | DiningPhilosophers.pickup(i); |
Synchronization within the Kernel
Synchronization in Windows
When the Windows kernel accesses a global resource
- on a single-processor system: it temporarily masks interrupts for all interrupt handlers that may also access the global resource.
- On a multiprocessor system: spinlock (Furthermore, for reasons of efficiency, the kernel ensures that a thread will never be preempted while holding a spinlock.)
For thread synchronization outside the kernel, Windows provides dispatcher objects. Using a dispatcher object, threads synchronize according to several different mechanisms, including mutex locks, semaphores, events, and timers.
Synchronization in Linux
The simplest synchronization technique within the Linux kernel is an atomic integer, which is represented using the opaque data type atomic_t.
To illustrate, consider a program that consists of an atomic integer counter and an integer value.
1 | atomic_t counter; |
The following code illustrates the effect of performing various atomic operations:
Atomic integers are particularly efficient in situations where an integer variable—such as a counter—needs to be updated, since atomic operations do not require the overhead of locking mechanisms. However, their use is limited to these sorts of scenarios.
Mutex locks are available in Linux for protecting critical sections within the kernel.
Linux also provides spinlocks and semaphores (as well as reader–writer versions of these two locks) for locking in the kernel.
Spinlocks—along with enabling and disabling kernel preemption—are used in the kernel only when a lock (or disabling kernel preemption) is held for a short duration. When a lock must be held for a longer period, semaphores or mutex locks are appropriate for use.
POSIX Synchronization
POSIX Mutex Locks
Mutex locks represent the fundamental synchronization technique used with Pthreads.
1 |
|
POSIX Semaphores
POSIX specifies two types of semaphores—named and unnamed.
POSIX Named Semaphores
1 |
|
In this instance, we are naming the semaphore SEM.
- The
O_CREATflag indicates that the semaphore will be created if it does not already exist. - Additionally, the semaphore has read and write access for other processes (via the parameter 0666)
- and is initialized to 1.
Advantage: multiple unrelated processes can easily use a common semaphore as a synchronization mechanism by simply referring to the semaphore’s name. (In the example above, once the semaphore SEM has been created, subsequent calls to sem_open() with the same parameters by other processes return a descriptor to the existing semaphore.)
