Multi-thread programming for APUE Learning (2): thread synchronization and apue multi-thread programming

Source: Internet
Author: User

Multi-thread programming for APUE Learning (2): thread synchronization and apue multi-thread programming
To ensure the security and reliability of critical resources, threads have to use locks. At the same time, only one or several threads are allowed to access variables. Common locks include mutex and read/write locks. Condition variable 1. mutex is expressed in pthread_mutex_t data type and must be initialized before use, you can set it to PTHREAD_MUTEX_INITIALIZER (only applicable to the mutex volume for static allocation), or call the pthread_mutex_init function to initialize it, and finally call pthread_mutex_destroy to release it.

#include <pthread.h>int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);int pthread_mutex_destroy(pthread_mutex_t *mutex);
To use the default attribute to initialize mutex, you only need to set attr to NULL. The mutex attribute will be discussed later. Use pthread_mutex_lock to lock the mutex. If the mutex is locked, the calling thread blocks the lock to the mutex. Use pthread_mutex_unlock to unlock the mutex. If the thread does not want to be blocked, it can call pthread_mutex_trylock to try to lock the mutex. If the mutex is not locked, the lock is successful. If the mutex is locked, pthread_mutex_trylock will fail and return EBUSY.
#include <pthread.h>int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_unlock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);
Example: # include <stdio. h> # include <pthread. h> struct foo {int f_count; pthread_mutex_t f_lock; int f_id ;}; struct foo * foo_alloc (int id) {struct foo * fp = NULL; if (fp = malloc (sizeof (struct foo )))! = NULL) {fp-> f_count = 1; fp-> f_id = id; if (pthread_mutex_init (& fp-> f_lock, NULL )! = 0) {free (fp); return NULL;} return fp;} void foo_hold (struct foo * fp) {pthread_mutex_lock (& fp-> f_lock ); fp-> f_count ++; pthread_mutex_unlock (& fp-> f_lock);} void foo_rele (struct foo * fp) {pthread_mutex_lock (& fp-> f_lock ); if (-- fp-> f_count = 0) {pthread_mutex_unlock (& fp-> f_lock); pthread_mutex_destroy (& fp-> f_lock); free (fp );} else {pthread_mutex_unlock (& fp-> f_lock );}}View Code the example above describes the mutex used to protect a data structure. we embed reference count in the object to ensure that before all threads that use this object complete data access, the memory space of this object will not be released. If the thread locks the same mutex twice, it will be in a deadlock state. If there is more than one mutex, and a thread can always occupy the first mutex, And it is blocked when trying to lock the second mutex, but the thread with the second mutex is also trying to lock the first mutex, and it is also blocked, so it will be deadlocked. You can carefully control the sequence of mutex lock to avoid deadlock. For example, all threads must first lock mutex A to lock mutex B. Another method is to release the mutex occupied by the thread when the thread cannot obtain the next mutex. Try again later. Example: # include "apue. h "# include <pthread. h> # define NMASH 29 # define HASH (id) (unsigned long) id) % NMASH) struct foo * fh [NMASH]; pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER; struct foo {int f_count; pthread_mutex_t f_lock; int f_id; struct foo * f_next;}; struct foo * foo_alloc (int id) {struct foo * fp = NULL; int idx = 0; if (fp = malloc (sizeof (struct foo )))! = NULL) {fp-> f_count = 1; fp-> f_id = if; if (pthread_mutex_init (& fp-> f_lock, NULL )! = 0) {free (fp); return NULL;} idx = HASH (id); pthread_mutex_lock (& hashlock); fp-> f_next = fh [idx]; fh [idx] = fp; pthread_mutex_lock (& fp-> f_lock); pthread_mutex_unlock (& hashlock); pthread_mutex_unlock (& fp-> f_lock);} return fp ;} void foo_hold (struct foo * fp) {pthread_mutex_lock (& fp-> f_lock); fp-> f_count ++; pthread_mutex_unlock (& fp-> f_lock );} struct foo * foo_find (int id) {struct foo * fp = NULL; pth Read_mutex_lock (& hashlock); for (fp = fh [HASH (id)]; fp! = NULL; fp = fp-> next) {if (fp-> f_id = id) {foo_hold (fp); break ;}} pthread_mutex_unlock (& hashlock); return fp ;} void foo_rele (struct foo * fp) {struct foo * tfp = NULL; int idx = 0; pthread_mutex_lock (& fp-> f_lock); if (fp-> f_count = 1) {pthread_mutex_unlock (& fp-> f_lock); pthread_mutex_lock (& hashlock); pthread_mutex_lock (& fp-> f_lock); if (fp-> f_count! = 1) {fp-> f_count --; pthread_mutex_unlock (& hashlock); pthread_mutex_unlock (& fp-> f_lock); return;} idx = HASH (fp-> f_id ); tfp = fh [idx]; if (tfp = fp) {fh [idx] = fp-> f_next} else {while (tfp-> next! = Fp) {tfp = tfp-> next;} tfp-> next = fp-> f_next;} pthread_mutex_unlock (& hashlock); pthread_mutex_unlock (& fp-> f_lock ); pthread_mutex_destroy (& fp-> f_lock); free (fp) ;}else {fp-> f_count --; pthread_mutex_unlock (& fp-> f_lock );}}The View Code example has a hash list and a mutex to protect the hash list. The lock order is hashlock and f_lock. Pay attention to this order, there will be no deadlocks, but this will also cause the code to be too cumbersome. The last function locks f_lock and re-locks f_lock. You need to re-examine the value of f_count, because it may be modified by other threads during this period. This method is too complicated. It is much easier to make hashlock protect f_cout. Example: # include "apue. h "# include <pthread. h> # define NMASH 29 # define HASH (id) (unsigned long) id) % NMASH) struct foo * fh [NMASH]; pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER; struct foo {int f_count; pthread_mutex_t f_lock; int f_id; struct foo * f_next;}; struct foo * foo_alloc (int id) {struct foo * fp = NULL; int idx = 0; if (fp = malloc (sizeof (struct foo )))! = NULL) {fp-> f_count = 1; fp-> f_id = if; if (pthread_mutex_init (& fp-> f_lock, NULL )! = 0) {free (fp); return NULL;} idx = HASH (id); pthread_mutex_lock (& hashlock); fp-> f_next = fh [idx]; fh [idx] = fp; pthread_mutex_lock (& fp-> f_lock); pthread_mutex_unlock (& hashlock); pthread_mutex_unlock (& fp-> f_lock);} return fp ;} void foo_hold (struct foo * fp) {pthread_mutex_lock (& hashlock); fp-> f_count ++; pthread_mutex_unlock (& hashlock);} struct foo * foo_find (int id) {struct foo * fp = NULL; pthread _ Mutex_lock (& hashlock); for (fp = fh [HASH (id)]; fp! = NULL; fp = fp-> next) {if (fp-> f_id = id) {foo_hold (fp); break ;}} pthread_mutex_unlock (& hashlock); return fp ;} void foo_rele (struct foo * fp) {struct foo * tfp = NULL; int idx = 0; pthread_mutex_lock (& hashlock); if (fp-> f_count = 1) {idx = HASH (fp-> f_id); tfp = fh [idx]; if (tfp = fp) {fh [idx] = fp-> f_next} else {while (tfp-> next! = Fp) {tfp = tfp-> next;} tfp-> next = fp-> f_next;} pthread_mutex_unlock (& hashlock); pthread_mutex_destroy (& fp-> f_lock ); free (fp) ;}else {fp-> f_count --; pthread_mutex_unlock (& hashlock );}}View Code when a thread attempts to obtain a locked mutex, The pthread_mutex_timedlock mutex primitive allows the thread to be bound for blocking time. Pthread_mutex_timedlock and pthread_mutex_lock are basically equivalent, but pthread_mutex_timedlock returns after the time-out period is reached. Timeout refers to the absolute waiting time. The timeout value is expressed in timespec.
#include <pthread.h>#include <time.h>int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);

 

Ii. read/write locks are similar to mutex volumes, but read/write locks allow higher concurrency. Only one thread can occupy the read/write locks in the write mode at a time, however, multiple threads can occupy the read/write locks in the Read mode at the same time. Simply put, it supports one writer and multiple readers. When the read/write lock is in the write/Write lock status, the thread attempting to lock the lock will be blocked. When the read/write lock is in the lock status, therefore, all threads trying to lock it in Read mode can obtain access permission, but the threads wishing to lock it in write mode will be blocked. However, when a thread attempts to obtain the lock in the write mode, the read/write lock will block the subsequent read Mode Lock requests to prevent the Read mode lock from occupying for a long time. Read/write locks are suitable for reading data structures more frequently than writing data. They are also known as shared mutex locks, Read shares, and write mutex.
#include <pthread.h>int pthread_rwlock_init(pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
The read/write lock calls phtread_rwlock_init for initialization. If you want the read/write lock to have a default attribute, pass null to attr. To lock the read/write lock in Read mode, call phtread_rwlock_rdlock. to lock the read/write lock in write mode, call pthread_rwlock_wrlock. However, you can call pthread_rwlock_unlock to lock the read/write lock.
#include <pthread.h>int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
Example: # include <stdio. h> # include <pthread. h> struct job {struct job * j_next; struct job * j_prev; pthread_t j_id ;}; struct queue {struct job * q_head; struct job * q_tail; pthread_rwlock_t q_lock ;}; int queue_init (struct queue * qp) {int err; qp-> q_head = NULL; qp-> q_tail = NULL; err = pthread_rwlock_init (& qb-> q_lock, NULL ); if (err! = 0) {return err;} return 0} void job_insert (struct queue * qp, struct job * jp) {pthread_rwlock_wrlock (& qb-> q_lock ); jp-> next = qp-> head; jp-> j_prev = NULL; if (qp-> q_head! = NULL) {qp-> q_head-> j_prev = jp;} else {qp-> tail = jp;} qp-> head = jp; pthread_rwlock_unlock (& qp-> q_lock);} void job_append (struct queue * qp, struct job * jp) {pthread_rwlock_wrlock (& qp-> q_lock); jp-> j_next = NULL; jp-> j_prev = qp-> tail; if (qp-> q_tail! = NULL) {qp-> q_tail-> j_next = jp;} qp-> q_tail = jp; pthread_rwlock_unlock (& qp-> q_lock);} void job_remove (struct queue * qp, struct job * jp) {pthread_rwlock_wrlock (& qp-> q_lock); if (jp = qp-> q_head) {qp-> q_head = jp-> j_next; if (qp-> q_tail = jp) {qp-> tail = NULL;} else {jp-> next-> j_prev = jp-> j_prev ;}} else if (jp = qp-> q_tail) {qp-> q_tail = jp-> j_prev; jp-> j_prev-> j_next = NULL;} else {Jp-> j_prev-> j_next = jp-> j_next; jp-> j_next-> j_prev = jp-> j_prev;} pthread_rwlock_unlock (& qp-> q_lock );} struct job * job_find (struct queue * qp, pthread_t id) {struct job * jp; if (pthread_rwlock_rdlock (& qp-> q_lock )! = 0) {return NULL;} for (jp = qb-> q_head; jp! = NULL; jp = jp-> j_next) {if (pthread_equal (jp-> j_id, id) {break ;}} pthread_rwlock_unlock (& qp-> q_lock ); return jp ;}The View Code is the same as the mutex. The read/write lock also has a read/write lock function with timeout to avoid permanent blocking.
#include <pthread.h>#include <time.h>int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr);int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr);

 

3. When condition variables are used together with mutex, the thread is allowed to wait for a specific condition in a non-competitive manner. The condition itself is protected by mutex. The thread must lock the mutex before changing the condition state. Before using the conditional variable, you must initialize it. You can assign the constant PTHREAD_CON_INITIALIZE to the statically assigned conditional variable, or use the pthread_cond_init function for initialization. Release with pthread_cond_destroy.
#include <pthread.h>int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);int pthread_cond_destroy(pthread_con_t *cond);
If you need a condition variable of the default attribute, you only need to assign null to attr. We use pthread_cond_wait to wait for the condition variable to be true. If it cannot be met within a given time, the error code is returned.
#include<pthread.h>int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex)int pthread_cond_timedwait(pthread_cond_t *restrict cond, phtread_mutex_t *restrict mutex, const struct timespec *restrict tsptr)
The caller passes the lock mutex to the function, and the function automatically puts the call thread on the list of threads waiting for the condition to unlock the mutex. When pthread_cond_wait returns, the mutex is locked again. Pthread_cond_timedwait has more time to wait. There are two functions that can be used to notify the thread that the condition has been met. The pthread_cond_signal function wakes up at least one thread, and pthread_cond_broadcast wakes up all threads waiting for the condition.
#include<phtread.h>int pthread_cond_signal(pthread_cond_t *cond)int pthread_cond_broadcast(pthread_cond_t *cond)
Example: # include <pthread. h> struct msg {struct msg * m_next;}; struct msg * workq; pthread_cond_t qready = PTHREAD_COND_INITIALIZER; pthread_mutex_t qlock = volume; void process_msg (void) {struct msg * mp; for (;) {pthread_mutex_lock (& qlock); while (workq = NULL) {pthread_cond_wait (& qready, & qlock);} mp = workq; workq = mp-> m_next; pthread_mutex_unlock (& qlock) ;}} void enqueue_msg (struct msg * mp) {pthread_mutex_lock (& qlock); mp-> m_next = workq; workq = mp; pthread_mutex_unlock (& qlock); pthread_cond_signal (& qready );}View Code

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.