Thread synchronization:
Synergetic, sequential access to public area data. Prevents data clutter and time-related errors.
Use of locks:
Protect public data. All threads should acquire locks before accessing public data. However, the lock itself is not mandatory.
mutex mutex
step:
1. pthread_mutex_t type.
2. pthread_mutex_t lock; create a lock
3. pthread_mutex_init; initialization
4. pthread_mutex_lock; lock, mutex– => 1 –> 0
5. Access shared data (stdout)
6. pthrad_mutext_unlock(); unlock, mutex + + => 0 –> 1
7. pthead_mutex_destroy; destroy lock
Initialize the mutex:
pthread_mutex_t mutex;
1. Dynamic initialization: pthread_mutex_init( & amp;mutex, NULL);
2. Static initialization: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
*Precautions:
Try to ensure the granularity of the lock, the smaller the better. (Lock before accessing shared data. Unlock [immediately] after accessing.)
A mutex is essentially a structure. We can see it as an integer. The initial value is 1. (The pthread_mutex_init() function call succeeds.)
Locking: –Operation, blocking thread.
Unlock: + + operation, wake up the thread blocked on the lock.
try lock: try to lock, success –. Fail, return. Also set the error number EBUSY
Read-write lock rwlock
There is only one lock. Lock data in read mode – read lock. Write locks to data – write locks.
Read shared, write exclusive. If the lock has been locked in read mode, then locking in read mode will succeed, and locking in write mode will block.
Write locks have high priority. Read locks and write locks request simultaneous blocking and give priority to satisfying write locks.
Compared with mutex, when there are many reading threads, the access efficiency is improved
pthread_rwlock_t rwlock; define lock
pthread_rwlock_init( & amp;rwlock, NULL); Initialize the lock
pthread_rwlock_rdlock( & amp;rwlock); Lock in the form of reading
pthread_rwlock_wrlock( & amp;rwlock); Lock in the form of writing
pthread_rwlock_unlock( & amp;rwlock); unlock
pthread_rwlock_destroy( & amp;rwlock); Destroy the lock
condition variable cond
Condition variable: It is not a lock itself, but it is usually used in conjunction with a mutex.
Main application functions:
pthread_cond_init(); initialization
pthread_cond_destroy(); destroy
pthread_cond_wait( &cond, &mutex);
effect:
1) Block waiting for the condition variable to be satisfied.
2) Unlock the successfully locked semaphore (equivalent to pthread_mutex_unlock( & amp;mutex)), 12 steps is an atomic operation.
3) When the condition is met and the function returns, unblock and re-apply for the mutex. Relock the semaphore (equivalent to, pthread_mutex_lock( & amp;mutex);).
pthread_cond_timedwait();
pthread_cond_signal(); Wake up (at least) one thread blocked on the condition variable.
pthread_cond_broadcast(); Wake up all threads blocked on the condition variable.
The return value of the above 6 functions is . It returns 0 successfully, and directly returns an error number if it fails.
The pthread_cond_t type is used to define condition variables.
pthread_cond_t cond;
Initialize the condition variable:
Dynamic initialization: pthread_cond_init( & amp;cond, NULL);
Static initialization: pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
Blocking wait condition:
pthread_cond_wait( &cond, &mutex);
effect:
1) Block waiting for the condition variable to be satisfied.
2) Unlock the successfully locked semaphore (equivalent to pthread_mutex_unlock( & amp;mutex)), 12 steps is an atomic operation.
3) When the condition is met and the function returns, unblock and re-apply for the mutex. Relock the semaphore (equivalent to, pthread_mutex_lock( & amp;mutex);).
Example: One producer with multiple consumers
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <pthread.h> void err_thread(int ret, char *str){ if (ret != 0) { fprintf(stderr, "%s:%s\\ ", str, strerror(ret)); pthread_exit(NULL); } } struct msg { int num; struct msg *next; }; struct msg *head; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // define/initialize a mutex pthread_cond_t has_data = PTHREAD_COND_INITIALIZER; // define/initialize a condition variable void *producer(void *arg){ while (1) { struct msg *mp = malloc(sizeof(struct msg)); mp->num = rand() % 1000 + 1; // Simulate the production of a data` printf("--produce %d\\ ", mp->num); pthread_mutex_lock( & amp;mutex); // lock mutex mp->next = head; // write public area head = mp; pthread_mutex_unlock( & amp;mutex); // unlock the mutex pthread_cond_signal( & amp;has_data); // wake up the thread blocked on the condition variable has_data. sleep(rand() % 3); } return NULL; } void *consumer(void *arg){ while (1) { struct msg *mp; pthread_mutex_lock( & amp;mutex); // lock mutex /* Both consumers are blocked on the condition variable, which means there is no data to consume. After the work is over, the lock is returned, and the producer hereby When a piece of data is produced, two consumers blocked by the condition variable will be woken up at the same time, and the two consumers will grab the lock when they are done. result That is, consumer A gets the lock and starts to consume data, while consumer B is blocked on the lock. After A finishes consuming the data, return the lock, B is woken up, but at this time there is no data for B to consume. So consumers blocking on condition variables should use while loop. In this way, after A consumes the data, the first thing B does is not to acquire the lock, but to determine the condition variable. */ while (head == NULL) { pthread_cond_wait( & amp;has_data, & amp;mutex); // block waiting for condition variable, unlock } // When pthread_cond_wait returns, rewrite the lock mutex mp = head; head = mp->next; pthread_mutex_unlock( & amp;mutex); // unlock the mutex printf("---------consumer id = %lu :%d\\ ", pthread_self(), mp->num); free(mp); sleep(rand()%3); } return NULL; } int main(int argc, char *argv[]){ int ret; pthread_t pid, cid; srand(time(NULL)); ret = pthread_create( & amp;pid, NULL, producer, NULL); // producer if (ret != 0) err_thread(ret, "pthread_create producer error"); ret = pthread_create( & amp;cid, NULL, consumer, NULL); // consumer if (ret != 0) err_thread(ret, "pthread_create consumer error"); ret = pthread_create( & amp;cid, NULL, consumer, NULL); // consumer if (ret != 0) err_thread(ret, "pthread_create consumer error"); ret = pthread_create( & amp;cid, NULL, consumer, NULL); // consumer if (ret != 0) err_thread(ret, "pthread_create consumer error"); pthread_join(pid, NULL); pthread_join(cid, NULL); return 0; }
Semaphore sem
Applied to synchronization between threads and processes.
Equivalent to a mutex initialized to N. The N value indicates the number of threads that can access the shared data area at the same time.
function:
sem_t sem; defines the type.
int sem_init(sem_t *sem, int pshared, unsigned int value);
parameter:
sem: semaphore.
pshared: 0: Used for inter-thread synchronization.
1: Used for inter-process synchronization.
value: N value. (specify the number of threads to access simultaneously)
sem_destroy();
sem_wait(); One call, one — operation, when the value of the semaphore is 0, — will block again.
(compared to pthread_mutex_lock)
sem_post(); One call, one ++ operation. When the value of the semaphore is N, another ++ will block.
(compared to pthread_mutex_unlock)