I. POSIX semaphores
For more information about semaphores, see here. System
V semaphores, now POSIX semaphores.
System V semaphores can only be used for synchronization between processes, while POSIX semaphores can be used for synchronization between processes. System V semaphore each PV operation can be N, but POSIX semaphore each PV can only be 1. In addition, POSIX semaphores are named and anonymous (MAN 7 sem_overview ):
1. Named semaphores
The name can be distinguished in the form of/somename. Only one/can exist, and the total length cannot exceed name_max-4 (generally 251 ).
You need to use the sem_open function to create or open the file. The PV operations are sem_wait and sem_post. You can use sem_close to close the file and use sem_unlink to delete the file.
2. Anonymous semaphores
It is stored in a shared memory. For thread sharing, this region can be a global variable. For process sharing, it can be System V shared memory (created by shmget and shmat ing ), it can also be POSIX shared memory (shm_open creation, MMAP ing ).
The anonymous semaphores must be initialized using sem_init. One of the parameters of sem_init determines whether thread sharing or process sharing. You can also use sem_post and sem_wait to perform operations. Before the shared memory is released, sem_destroy must be used for anonymous semaphores
Destroy.
The specific parameters of these functions can be man.
Ii. mutex lock
For multi-threaded programs, access conflicts are common. The solution is to introduce mutex and mutualexclusive locks ), the thread that acquires the lock can complete the "Read-Modify-write" Operation and release the lock to other threads. The thread that does not obtain the lock can only wait but cannot access the shared data, in this way, the "Read-Modify-write" three steps constitute an atomic operation, either executed or not executed, and will not be interrupted in the middle, this operation will not be performed concurrently on other processors.
Mutex is represented by a variable of the pthread_mutex_t type. The pthread_mutex_init function initializes mutex. the ATTR parameter sets the mutex attribute. If ATTR is null, it indicates the default attribute. For details, refer to the struct:
C ++ code
1 2 3 4 5 6 7 8 9 10 |
|
Struct pthread_mutexattr_t { Enum lock_type // use pthread_mutexattr_settype to change { Pthread_mutex_timed_np [Default] // when a thread locks, other threads requesting the lock form a waiting queue. After unlocking, the lock is obtained by priority. Pthread_mutex_adaptive_np // The simplest lock type of the action. After unlocking, all threads will compete again. Pthread_mutex_recursive_np // The same thread is allowed to successfully obtain the same lock multiple times. Of course, you also need to unlock it multiple times. Other threads re-compete When unlocking. Pthread_mutex_errorcheck_np // if the same thread requests the same lock, edeadlk is returned. Otherwise, the action is the same as that of pthread_mutex_timed_np. } Type; } ATTR; |
The mutex initialized by the pthread_mutex_init function can be destroyed by pthread_mutex_destroy. If the mutex variable is statically allocated (Global or static), you can also use a macro to define pthread_mutex_initializer for initialization, which is equivalent to pthread_mutex_init for initialization and the ATTR parameter is null.
One thread can call pthread_mutex_lock to obtain the mutex. If another thread has already called pthread_mutex_lock to obtain the mutex, the current thread needs to wait until another thread calls pthread_mutex_unlock to release the mutex. The current thread is awakened, to obtain the mutex and continue the execution.
The specific function above can be man.
Iii. producer and consumer issues
For more information about producer and consumer problems, see here. The following uses POSIX semaphores and mutex lock for Demonstration:
C ++ code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
|
# Include <unistd. h> # Include <sys/types. h> # Include <pthread. h> # Include <semaphore. h># Include <stdlib. h> # Include <stdio. h> # Include <errno. h> # Include <string. h> # Define err_exit (m )\ Do \ {\ Perror (m );\ Exit (exit_failure );\ } While (0) # Define consumers_count 1 # Define producers_count 1 # Define buffsize 10 Int g_buffer [buffsize]; Unsigned short in = 0; Unsigned short out = 0; Unsigned short produce_id = 0; Unsigned short consume_id = 0; Sem_t g_sem_full; Sem_t g_sem_empty; Pthread_mutex_t g_mutex; Pthread_t g_thread [consumers_count + producers_count]; Void * consume (void * Arg) { Int I; Int num = (INT) ARG; While (1) { Printf ("% d wait buffer not empty \ n", num ); Sem_wait (& g_sem_empty ); Pthread_mutex_lock (& g_mutex ); For (I = 0; I <buffsize; I ++) { Printf ("% 02d", I ); If (g_buffer [I] =-1) Printf ("% s", "null "); Else Printf ("% d", g_buffer [I]); If (I = out) Printf ("\ t <-- consume "); Printf ("\ n "); } Consume_id = g_buffer [out]; Printf ("% d begin consume product % d \ n", num, consume_id ); G_buffer [out] =-1; Out = (out + 1) % buffsize; Printf ("% d end consume product % d \ n", num, consume_id ); Pthread_mutex_unlock (& g_mutex ); Sem_post (& g_sem_full ); Sleep (1 ); } Return NULL; } Void * produce (void * Arg) { Int num = (INT) ARG; Int I; While (1) { Printf ("% d wait buffer not full \ n", num ); Sem_wait (& g_sem_full ); Pthread_mutex_lock (& g_mutex ); For (I = 0; I <buffsize; I ++) { Printf ("% 02d", I ); If (g_buffer [I] =-1) Printf ("% s", "null "); Else Printf ("% d", g_buffer [I]); If (I = in) Printf ("\ t <-- produce "); Printf ("\ n "); } Printf ("% d begin produce product % d \ n", num, produce_id ); G_buffer [in] = produce_id; In = (in + 1) % buffsize; Printf ("% d end produce product % d \ n", num, produce_id ++ ); Pthread_mutex_unlock (& g_mutex ); Sem_post (& g_sem_empty ); Sleep (5 ); } Return NULL; } Int main (void) { Int I; For (I = 0; I <buffsize; I ++) G_buffer [I] =-1; Sem_init (& g_sem_full, 0, buffsize ); Sem_init (& g_sem_empty, 0, 0 ); Pthread_mutex_init (& g_mutex, null ); For (I = 0; I <consumers_count; I ++) Pthread_create (& g_thread [I], null, consume, (void *) I ); For (I = 0; I <producers_count; I ++) Pthread_create (& g_thread [consumers_count + I], null, produce, (void *) I ); For (I = 0; I <consumers_count + producers_count; I ++) Pthread_join (g_thread [I], null ); Sem_destroy (& g_sem_full ); Sem_destroy (& g_sem_empty ); Pthread_mutex_destroy (& g_mutex ); Return 0; } |
Compared with the program here, the program logic does not change much, but it is replaced by pthread_mutex_lock.
Sem_mutex, followed by the synchronization between demo threads. Currently, the producer and consumer of the preceding program have one thread each, but the producer's sleep time is five times that of the consumer, so the consumer will often block in sem_wait (& g_sem_empty) as the buffer is often empty, you can change productors_count to 5, that is, five producer threads and one consumer thread, and the producer's sleep time is five times that of the consumer, from the dynamic output, we can see that there is basically a dynamic balance, that is, five producers have produced five items at once, and the consumer consumes one copy for 1 second, just before the producer continues production.
Iv. Introduction to spin locks and read/write locks
(1) spin locks
A spin lock is similar to a mutex lock, and has higher performance than a mutex lock.
A very important difference between a spin lock and a mutex lock is that when a thread applies for a spin lock, the thread will not be suspended, and it is in a busy waiting state, which is generally used in the case of a short wait time.
Pthread_spin_init
Pthread_spin_destroy
Pthread_spin_lock
Pthread_spin_unlock
(2) read/write locks
1. As long as no thread holds the given read/write lock for writing, any number of threads can hold the read/write lock for reading
2. Only when no thread holds a given read/write lock for reading or writing can the read/write lock be assigned for writing
3. read/write locks are used to read shared locks, while read/write locks are used to write exclusive locks.
Pthread_rwlock_init
Pthread_rwlock_destroy
Int pthread_rwlock_rdlock
Int pthread_rwlock_wrlock
Int pthread_rwlock_unlock
Refer:
Linux C Programming one-stop learning
UNP