do {
flag[0] = true;// First, P0 raises his hand to indicate that I want to visit
while(flag[1]) {//Check if P1 has raised his hand
if(turn==1){// If P1 also raises his hand, then see whose turn it is
flag[0]=false;// If it is indeed P1's turn, then P0 puts his hand down first (let P1 go first)
while(turn==1);// As long as it is still time for P1, P0 will not raise its hand and keep waiting
flag[0]=true;// Wait until P1 is used up (it's P0's turn), and P0 raises its hand again
}
flag[1] = false; // As long as you can jump out of the loop, it means that P1 is used up, and you should jump out of the outermost circle of while
}
critical section;// access critical section
turn = 1;// After P0 visits, give the turn to P1, so that P1 can visit
flag[0]=false;// P0 put down
remainder section;
} while(true);
A shared "turn" variable and a number of threads arranged in a circular manner.
Each thread has a flag indicating its desire to enter the critical section
If multiple threads want to enter the critical section, they move forward in order and the turn variable is updated to the next thread's value after each thread completes execution
ENTRY PROTOCOL(for Process i):
repeat {//Whether there is a request process from turn to i: if it exists, it will continue to loop until there is no such process, and mark the current process as ACTIVE
flags[i] = WAITING;//Indicate that you need resources
index = turn;//whose turn is itwhile (index != i) {// from turn to i take turns to find idle threadsif (flag[index] != IDLE) index = turn;//turn to i has non-idle blockingelse index = (index+1) mod n; //Otherwise it is i's turn and jump out
}
flags[i] = ACTIVE;//Pi active; other threads may be active// Make further judgments on all ACTIVE processes, and judge whether there are other ACTIVE processes besides the current process
index = 0;//Check if there are other active oneswhile ((index < n) && ((index == i) || (flags[index] != ACTIVE))) {
index = index+1;
}//If there is no active later, and it's Pi's turn or turn idle, it's i's turn; otherwise continue to loop
} until ((index >= n) && ((turn == i) || (flags[turn] == IDLE)));
turn = i;//get turn and process
Method 2: Software-based solution -- N threads
EXIT PROTOCOL(for Process i ):
index = turn+1 mod n;//Find one that is not idlewhile (flags[index] == IDLE) {
index = index+1 mod n;
}
turn = index;//Find the one that is not idle and set it to turn; or set it to yourself
flag[i] = IDLE;//end, become idle
Method 3: Higher-level abstraction method
Software based solution
Complex and involves busy waiting
Higher level abstract method
Hardware provides some synchronization primitives
Disabling interrupts and atomic instructions, etc.
The OS provides higher-level programming abstractions to simplify thread synchronization
eg: locks, semaphores
Using hardware primitives
Method 3: Higher-level abstraction method -- Lock
A lock is an abstract data structure
A binary variable (locked/unlocked)
Locks are used to control access to critical sections
Lock::Acquire()
Waits until the lock is released, then acquires it
Lock::Release()
Releases the lock and wakes up any waiting threads
Method 3: Higher-level abstraction method -- Lock
Modern CPUs provide special atomic instructions
Atomic instructions
Test-and-Set
Read a value from a memory location
Test if the value is 1 (returns true or false)
Set the memory location to 1
Enter 0, change to 1, and return 0;
Enter 1, keep 1, and return 1;
Method 3: Higher-level abstraction method -- Lock
Modern CPUs provide special atomic instructions
do {
while(TestAndSet(&lock));
critical section;
lock = false;
remainder section;
} while (true)
Method 3: Higher-level abstraction method -- Lock
Modern CPUs provide special atomic instructions
do {
while(TestAndSet(&lock));
critical section;
lock = false;
remainder section;
} while (true)
bool compare_and_swap(int *value, int old, int new) {
if(*value==old) {
*value = new;
return true; }
return false;
}
int lock = 0; // Initially the lock is free
while(!compare_and_swap(&lock,0,1)); // lock lock
critical section;
lock=0; // unlock unlock
remainder section;
Method 3: Higher-level abstraction method -- Lock
Atomic operation: Compare and Swap (CaS)
ABA problem:
value = 100;
Thread1: value - 50; //Successful value=50
Thread2: value - 50; //Blocked
Thread3: value + 50; //Successful value=50
Thread2: Retry successful
Solution idea: Add a version number (timestamp)
(100,1); (50,2); (100,3)
Method 3: Higher-level abstraction method -- Lock
Implementing spinlocks using the Test-and-Set (TaS) instruction
Threads consume CPU time while waiting
Method 3: Higher-level abstraction method -- Lock
Busy-wait locks vs. waiting locks
Method 3: Higher-level abstraction method -- Lock
Advantages
Applicable to any number of thread synchronization in single processors or shared-memory multiprocessors
Simple and easy to prove
Supports multiple critical sections
Disadvantages
Busy waiting consumes processor time
May result in starvation
Multiple waiting threads when a thread exits the critical section
Possible deadlock: Threads waiting for each other, unable to proceed
Summary
Three commonly used synchronization implementation methods
Disabling interrupts (for single processors only)
Software-based methods (complex)
Locks as a higher-level synchronization abstraction
Hardware atomic instructions (applicable to single or multiprocessor systems)
Locks can be used to achieve mutual exclusion
---
### Interaction between progress/thread: degree of mutual awareness

---
### Interaction between progress/thread: degree of mutual awareness

---
### Interaction between progress/thread: degree of mutual awareness

---
### Method 3: More advanced abstract method -- lock (lock)
- Atomic operation: exchange instruction CaS (Compare and Swap)
```
bool compare_and_swap(int *value, int old, int new) {
if(*value==old) {
*value = new;
return true;
}
return false;
}
```
```
lock(): while(!compare_and_swap(&lock,0,1));
critical section;
unlock(): lock=0;
```
What is CAS? How should the ABA problem be understood? https://zhuanlan.zhihu.com/p/139635112
https://www.zhihu.com/question/23281499/answer/24112589
Regarding the ABA problem, I thought of an example: when you are very thirsty, you find a cup full of water, and you drink it up. Then refill the glass with water. Then you leave, and when the real owner of the glass comes back and sees that the glass is still full of water, of course he doesn't know if someone has drunk it and refilled it. One strategy for a solution to this problem is to assume that an automated recorder records each pour, so the owner can tell when she returns if a refill has occurred since she left. This is also the strategy currently used to solve the ABA problem.
### Method 3: More advanced abstract method -- lock (lock)
Modern CPU architectures provide some special atomic operation instructions
- Atomic operation instructions
- exchange command (exchange)
- swap two values in memory
