1. When is the SEM insufficient? Do I still need to use mutex?
Assume that there is a shared resource sum, and the associated mutex is lock_s. assume that the sum operation of each thread is very simple and irrelevant to the sum State. For example, it is only sum ++. only mutex is enough. the programmer only needs to ensure that the lock is obtained before each thread operation, sum ++, and unlock. the code for each thread will look like this:
Add () <br/>{< br/> pthread_mutex_lock (lock_s); <br/> sum ++; <br/> pthread_mutex_unlock (lock_s); <br/>}
If the operation is complex, assume that the operation of thread T0, T1, and T2 is sum ++, and thread T3 prints a message when sum reaches 100, and clears sum. in this case, if only mutex is used, T3 requires a loop. In each loop, lock_s is obtained first, and the sum state is checked. If sum> = 100, the data is printed and cleared, then unlock. if sum & lt; 100, then unlock and sleep () This thread is appropriate for a period of time. at this time, the code for T0, T1, and T2 remains unchanged. The code for T3 is as follows:
Print () <br/>{< br/> while (1) <br/>{< br/> pthread_mutex_lock (lock_s); <br/> If (sum <100) <br/>{< br/> printf ("sum reach 100! "); <Br/> pthread_mutex_unlock (lock_s); <br/>}< br/> else <br/>{< br/> pthread_mutex_unlock (lock_s ); <br/> my_thread_sleep (100); <br/> Return OK; <br/>}< br/>}
There are two problems with this method.
1) sum will not reach 100 in most cases, so for the T3 code, in most cases, it takes the else branch, only lock and unlock, and then sleep (). this wastes CPU processing time.
2) To save CPU processing time, T3 will sleep () for a period of time when the sum has not reached 100. this brings about another problem, that is, the T3 response speed decreases. t4 may wake up when sum reaches 200.
3) in this way, the programmer is in a dilemma when setting sleep () time. It is too short to save resources, and it is too long to reduce the response speed. This is really hard to do!
At this time, condition variable underpants fell from the sky, saving you!
You first define a condition variable,
Pthread_cond_t cond_sum_ready = pthread_cond_initializer;
T0, T1, and T2 only need to be followed by two lines, like this:
Add () <br/>{< br/> pthread_mutex_lock (lock_s); <br/> sum ++; <br/> pthread_mutex_unlock (lock_s ); <br/> If (sum> = 100) <br/> pthread_cond_signal (& cond_sum_ready); <br/>}
The Code of T3 becomes:
Print <br/>{< br/> pthread_mutex_lock (lock_s); <br/> while (sum <100) <br/> pthread_cond_wait (& cond_sum_ready, & lock_s ); <br/> printf ("sum is over 100! "); <Br/> sum = 0; <br/> pthread_mutex_unlock (lock_s); <br/> Return OK; <br/>}
Note:
1) Before thread_cond_wait (), you must first lock the associated mutex, because if the target condition is not met, pthread_cond_wait () will actually unlock the mutex, and then block, lock the mutex again after the target condition is met, and then return.
2) Why is it while (sum <100) instead of IF (sum <100 )? This is because there is a time difference between pthread_cond_signal () and pthread_cond_wait (). In this case, there is another thread T4 that reduces sum to less than 100, after pthread_cond_wait () is returned, T3 should check the sum size again. this is the function of using while.
Others:
Instructions on x86 atomic operation commands
Cmpxchg compares and exchanges commands with the following semantics:
Int compareandexchange (int * PTR, int old, int new) <br/>{< br/> int actual = * PTR; <br/> If (actual = old) <br/> * PTR = new; <br/> return actual; <br/>}
Description in the Inter White Paper:
(* Accumulator = Al, ax, eax, or Rax depending on whether a byte, word, doubleword, or <br/> quadword comparison is being completed MED *) <br/> If accumulator = DEST <br/> then <br/> ZF limit 1; <br/> DEST limit SRC; <br/> else <br/> ZF defaults 0; <br/> accumulator implements DEST; <br/> fi;
This atomic operation can be used to implement spin locks. This is described in the previous article:
Void lock (lock_t * Lock) {<br/> while (compareandexchange (& lock-> flag, 0, 1) = 1) <br/>; // spin <br/>}< br/> void unlock (lock_t * Lock) {<br/> lock-> flag = 0; <br/>}
Description of atomic operations under SMP:
Atomic operations are inseparable and will not be interrupted by any other tasks or events after execution. In a single processor system (uniprocessor), operations that can be completed in a single command can be considered as "Atomic operations", because interruptions can only occur between commands. This is also why test_and_set, test_and_clear and other commands are introduced in some CPU command systems for the critical resource mutex. The symmetric multi-processor structure is different. Because multiple processors in the system run independently, operations that can be completed in a single command may be affected.
On the X86 platform, the CPU provides a means to lock the bus during command execution. There is a lead on the CPU chip # hlock pin. If the assembly language program adds the prefix "Lock" before a command ", the compiled machine code lowers the potential of the # hlock pin when the CPU executes this command until the end of this command, thereby locking the bus, in this way, other CPUs on the same bus cannot access the memory temporarily through the bus, ensuring the atomicity of this command in a multi-processor environment.
Of course, not all commands can be prefixed with lock. Only add, ADC, And, BTC, BTR, BTS, cmpxchg, Dec, Inc, neg, not, or, you can add the lock command before SBB, sub, XOR, xadd, and xchg commands to perform atomic operations.
Others:
Spin lock: Requests resources all the time. In this process, the CPU cannot schedule other tasks for processing, so it consumes CPU resources.
Mutex lock: Requests resources first. If the resource fails, context switching is performed immediately. At this time, the CPU can schedule other tasks for processing.
Thanks for the following Blog content!
Http://www.eetop.cn/blog/html/04/343504-14125.html
Http://dev.firnow.com/course/6_system/linux/Linuxjs/20090901/173322.html