Thread synchronization - read-write lock

Lock write (reader-writer lock)

  Lock write (reader-writer lock) a mutex and the like, but the read-write lock allows for higher parallelism. Mutex is either locked state, or is not locked state, but only one thread can lock it. And read-write locks can have three states: locked status read mode, write mode locked state and an unlocked state. Only one thread can occupy write mode read-write locks, but multiple threads can occupy read-write lock mode at the same time.

  When the read-write lock is write locked state before the lock is released, it blocked all attempts to lock this thread will be locked. When the read-write lock in the locked state, all its attempts to read mode for locking thread can get access, but any hope in write mode lock this thread will be locked blocked until all the threads release Until they read lock. Although each operating system to read and write different from each lock, but when the read-write lock in read mode locked state, but this time there is a thread tries to acquire the lock in write mode, usually read-write lock will block the subsequent read mode lock requests. This mode locks long-term occupation read, write mode while waiting for the lock request has not met.

Read-Write Lock applicable

  The number of read-write locks ideally suited for data reading is much larger than writes. When the read-write lock locked state in write mode, it is said that data protection can be safely modified because only one thread can have read-write locks in write mode. When the lock is in the locked state at the read write mode, as long as the first thread acquiring a write lock in the read mode, the lock protected data can be read mode to obtain a plurality of read thread lock.

  Also known as shared read-write lock mutex (shared-exclusive lock). When the read-write lock mode is locked, it is said to be locked in shared mode. Write mode when it is locked, it can be said to be mutually exclusive mode is locked.

Lock API functions to read and write

// header 
#include <pthread.h> 
#include <time.h> the timespec structure struct // 

// write lock API function 
int to pthread_rwlock_init (* pthread_rwlock_t the restrict rwlock is acquired, the restrict const pthread_rwlockattr_t * attr); 
int pthread_rwlock_destroy (pthread_rwlock_t rwlock is acquired *); 
int the pthread_rwlock_wrlock (pthread_rwlock_t * rwlock is acquired); // write mode locking function 
int the pthread_rwlock_rdlock (pthread_rwlock_t * rwlock is acquired); // read mode locking function 
int the pthread_rwlock_unlock (pthread_rwlock_t * rwlock is acquired); // read-write lock releasing function 
int pthread_rwlock_trywrlock (pthread_rwlock_t * rwlock is acquired); 
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);

Write lock initialization / destruction

  And the same mutex, read-write locks must be initialized before use, they must be destroyed before releasing the underlying memory.

#include <pthread.h> 
int to pthread_rwlock_init (pthread_rwlock_t the restrict rwlock is acquired *, const * pthread_rwlockattr_t the restrict attr); 
Parameters 
rwlock: Pointer read-write lock. 
attr: write lock attributes. If the parameter value is NULL, indicating create a default attribute of read-write locks. 
[Description] pthread_rwlock_init function dynamically creates and initializes a read-write lock. 

int pthread_rwlock_destroy (pthread_rwlock_t * rwlock); 
[Description] rwlock destruction write lock pointer. 
Return Value [] function returns two values: successful, returns 0; failure error code is returned. 

[] Of extended write lock allocated statically initialize method pthread_rwlock_t rwlock is acquired
 = PTHREAD_RWLOCK_INITIALIZER ;

[Description] Before use read-write lock memory occupied, pthread_rwlock_destroy need to call a function to do cleanup work. If pthread_rwlock_init function to read-write lock allocation of resources, pthread_rwlock_destroy function will release these resources. If pthread_rwlock_destroy before calling on the release of the memory space occupied by the read-write lock, then the lock allocation of resources will be lost.

Write lock lock / lock

  Read-Write Lock in read mode, you need to call pthread_rwlock_rdlock function. To lock a read-write lock in write mode, you need to call pthread_rwlock_wrlock function. Regardless of the manner in which lock read-write locks can call pthread_rwlock_unlock unlock function.

#include <pthread.h> 
int the pthread_rwlock_wrlock (pthread_rwlock_t * rwlock is acquired); // write mode locking function 
int the pthread_rwlock_rdlock (pthread_rwlock_t * rwlock is acquired); // read mode locking function 
int the pthread_rwlock_unlock (pthread_rwlock_t * rwlock is acquired); // write released lock function 

[return value] all functions return values: success, returns 0; failed with error code.

  Single UNIX Specification version also defines the conditions for read-write lock primitives.

#include <pthread.h> 
int pthread_rwlock_trywrlock (pthread_rwlock_t * rwlock is acquired); 
int pthread_rwlock_tryrdlock (pthread_rwlock_t * rwlock is acquired);
 
[Return] Return value of values of two functions: successful, returns 0; failure error code is returned. 
[Description] These two functions are read-write lock condition release, when locks can be acquired, the return value is 0, the lock can not be acquired, the thread will not block, but returns an error code directly EBUSY.

 

With a read-write lock timeout

  And the same mutex, Single UNIX Specification provides a read-write locks lock with a timeout function that enables applications to avoid falling into a state of permanent obstruction in acquiring read-write lock.

#IN in- clude <pthread.h> 
#include <time.h> 
int pthread_rwlock_timedrdlock (pthread_rwlock_t the restrict rwlock is acquired *, const struct abs_timeout the restrict the timespec *); 
int pthread_rwlock_timedwrlock (* pthread_rwlock_t the restrict rwlock is acquired, the timespec * const struct the restrict abs_timeout);
 
[Return value two return value]: successful, returns 0; failure error code is returned. 
[Description] behavior of these two functions with their "untimed" version similar. abs_timeout parameter points to struct timespec structure, should be specified thread blocked time. If the thread does not get read-write lock, then when the timeout expires, 
these two functions will return ETIMEDOUT error. And pthread_mutex_timedlock function is similar to the specified timeout time is absolute, rather than relative time.

Example 1: Method using read-write lock. In this example, a plurality of single worker thread main thread acquires the job assigned to them, by a read-write lock request queue job protection.

  1 #include <stdlib.h>
  2 #include <pthread.h>
  3 
  4 struct job {
  5     struct job *j_next;
  6     struct job *j_prev;
  7     pthread_t   j_id;   /* tells which thread handles this job */
  8     /* ... more stuff here ... */
  9 };
 10 
 11 struct queue {
 12     struct job      *q_head;
 13     struct job      *q_tail;
 14     pthread_rwlock_t q_lock;
 15 };
 16 
 17 /*
 18  * Initialize a queue.
 19  */
 20 int
 21 queue_init(struct queue *qp)
 22 {
 23     int err;
 24 
 25     qp->q_head = NULL;
 26     qp->q_tail = NULL;
 27     //初始化读写锁
 28     err = pthread_rwlock_init(&qp->q_lock, NULL);
 29     if (err != 0)
 30         return(err);
 31     / * ... ... Continue Initialization * / 
32      return ( 0 );
 33 is  }
 34 is  
35  / * 
36  . The AT * the Insert A Job Queue The head of
 37 [   * / 
38 is  void 
39 job_insert ( struct Queue QP *, struct * job JP)
 40  {
 41 is      the pthread_rwlock_wrlock (& a qp-> q_lock); // read and write locks write lock 
42 is      jp-> = a qp-j - next> q_head; // be inserted j - next job in the queue pointers to the current a first job node 
43 is      jp-> j_prev = NULL;
 44 is     IF (! a qp-> q_head = NULL) // if the queue is not empty 
45          a qp-> q_head-> = j_prev JP; // current j_prev queue head pointer pointing to the node to be inserted job node 
46 is      the else  // If the queue is empty 
47          a qp-> = q_tail JP; // queue tail pointer to point to the new job also q_tail node 
48      a qp-> = q_head JP; // queue head pointer point to the newly inserted q_head node job, the new job i.e. node is inserted into the head of the queue 
49      the pthread_rwlock_unlock (& a qp-> q_lock); // read and write unlock the lock 
50  }
 51 is  
52 is  / * 
53 is  * the Append the oN a tail of the Job queue.
 54 is   * / 
55  void 
56 is job_append (struct Queue QP *, struct Job * JP)
 57 is  {
 58      the pthread_rwlock_wrlock (& a qp-> q_lock); // read and write locks write lock 
59      jp-> j - next = NULL;
 60      jp-> = a qp-j_prev> q_tail ; // queue tail pointer before the current point to the end node following the node 
61 is      IF (a qp-> q_tail =! NULL)
 62 is          a qp-> q_tail-> = j - next JP; // current queue tail node to the new node j - next point JP 
63 is      the else 
64          a qp-> JP q_head =;     / * List WAS empty * / 
65      a qp-> = q_tail JP; // queue tail pointer to the new node 
66     the pthread_rwlock_unlock (& a qp-> q_lock);
 67  }
 68  
69  / * 
70  * the Remove The GIVEN Job from A Queue.
 71 is   * / 
72  void 
73 is job_remove ( struct Queue * QP, struct Job * JP)
 74  {
 75      the pthread_rwlock_wrlock (& a qp-> q_lock);
 76      IF (a qp-jp ==> q_head) { // be deleted queue node is the head node jp 
77          a qp-> q_head = jp-> j - next; // queue head pointer to the successor node jp 
78          IF (a qp-> == q_tail JP)
 79             a qp-> q_tail = NULL;
 80          the else 
81              jp-> j_next-> j_prev = jp-> j_prev; // before j_prev successor node pointing jp jp relay node 
82      } the else  IF (a qp-jp ==> q_tail) { // be deleted queue node is the end node jp 
83          a qp-> q_tail = jp-> j_prev; // front end of the queue pointer to the following node jp 
84          jp-> j_prev-> = j - next jp-> j - next; // before jp j - next relay node point jp successor node 
85      } the else {
 86          jp-> j_prev-> = j - next jp-> j - next; // before jp j - next relay node of the successor node point jp point 
87          jp-> j_next-> j_prev = jp-> j_prev;// before j_prev directed jp jp successor node relay node 
88      }
 89      the pthread_rwlock_unlock (& a qp-> q_lock);
 90  }
 91 is  
92  / * 
93  . * The GIVEN for the Find A Job ID Thread
 94   * / 
95  struct Job *
 96 job_find ( struct Queue * QP, pthread_t ID)
 97  {
 98      struct Job * JP;
 99  
100      IF (! the pthread_rwlock_rdlock (& a qp-> q_lock) = 0 ) // read and write locks read lock 
101          return (NULL );
 102 
103     for (jp = qp->q_head; jp != NULL; jp = jp->j_next)
104         if (pthread_equal(jp->j_id, id))
105             break;
106 
107     pthread_rwlock_unlock(&qp->q_lock);
108     return(jp);
109 }
rwlock.c

【analysis】

1. increase job or delete jobs from the queue to the queue, the write mode are used to lock the queue read-write lock.

2. When the search queue, using the read write lock mode to lock the queue, allowing all worker threads can concurrently search queue.

3. A worker thread can only read them with thread ID matches the job from the queue. Because the job structure at the same time can only be used by one thread, so no additional locking.

4. Only the frequency of job search in a thread queue is much higher than the increase or delete the job, read-write locks can improve performance.

 

Guess you like

Origin www.cnblogs.com/yunfan1024/p/11306599.html