Mutex and read-write lock operation functions

    Mutex variables are represented by the pthread_mutex_t data type. The following functions operate on mutexes.
#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);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
                         /* Return value of all functions: if successful, return 0; otherwise, return error number */

    Before using a mutex, it must be initialized first. It can be set to the constant PTHREAD_MUTEX_INITIALIZER (only for statically allocated mutex), or it can be initialized through the pthread_mutex_init function. To initialize a mutex with default attributes, simply set the attr parameter of pthread_mutex_init to NULL. If the mutex is dynamically allocated, pthread_mutex_destroy needs to be called before freeing the memory.
    The functions pthread_mutex_lock and pthread_mutex_unlock can lock and unlock the mutex, respectively. If the mutex is already locked, the calling thread will block until the mutex is unlocked. If the thread does not want to be blocked, it can try to lock the mutex using pthread_mutex_trylock. If the mutex is not locked when pthread_mutex_trylock is called, the function locks the mutex and returns 0 directly, otherwise the function fails and returns EBUSY.
    The following code describes how to protect a data structure with a mutex. When multiple threads want to access a dynamically allocated object, you can embed a reference count in the object to ensure that the object's memory space is not freed until all threads using the object have finished accessing the data.
#include <stdlib.h>
#include <pthread.h>

struct foo{
	int	f_id;
	int	f_count;
	pthread_mutex_t	f_lock;
	/*...more stuff here...*/
};

struct foo* foo_alloc(int id){	// allocate the object
	struct foo *foop;
	if((foop=molloc(sizeof(struct foo))) != NULL){
		foop->f_id = id;
		foop->f_count = 1;
		if(pthread_mutex_init(&foop->f_lock, NULL) != 0){
			free(foop);
			return NULL
		}
		/*...continue initialization...*/
	}
	return foop;
}

void foo_hold(struct foo *foop){	// add a reference to the object
	pthread_mutex_lock(&foop->f_lock);
	foop->f_count++;
	pthread_mutex_unlock(&foop->f_lock);
}

void foo_rele(struct foo *foop){	// release a reference to the object
	pthread_mutex_lock(&foop->f_lock);
	if(--foop->f_count == 0){
		pthread_mutex_unlock(&foop->f_lock);
		pthread_mutex_destroy(&foop->f_lock);
		free(foop);
	}else{
		pthread_mutex_unlock(&foop->f_lock);
	}
}

    To avoid a thread blocking forever when acquiring a lock, the function pthread_mutex_timedlock can be used.
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);
                            /* Return value: if successful, return 0; otherwise, return error number */

    This function is basically equivalent to pthread_mutex_lock, but it allows bound thread blocking time. When the timeout is reached, it will not lock the mutex, but return the error code ETIMEDOUT. The time parameter here is an absolute time.
    The following example demonstrates how to use pthread_mutex_timedlock to avoid permanent blocking by relocking an existing mutex (which usually results in a deadlock).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>

void pr_time(struct timespec tout){
	char buf[64];
	struct tm *tmp = localtime(&tout.tv_sec);
	strftime(buf, sizeof(buf), "%r", tmp);
	printf("current time is: %s\n", buf);
}

int main(void){
	pthread_mutex_t	lock = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_lock(&lock);
	printf("mutex is locked\n");
	struct timespec tout;
	clock_gettime(CLOCK_REALTIME, &tout);
	tout.tv_sec += 5;			// 5 seconds from now
	// caution: this could lead to deadlock
	int err = pthread_mutex_timedlock(&lock, &tout);
	clock_gettime(CLOCK_REALTIME, &tout);
	pr_time(all);
	pr_time(all);
	if(err != 0)
		printf("can't lock mutex again: %s\n", strerror(err));
	else
		printf("mutex locked again!\n");
	exit(0);
}

    After compiling run:
$ gcc timeoutLock.c -lpthread -lrt -o timeoutLock.out
$ ./timeoutLock.out
mutex is locked
current time is: 10:37:07 AM
current time is: 10:37:12 AM
can't lock mutex again: Connection timed out

    After talking about mutexes, let's talk about read-write locks.
    Read-write locks, also known as shared mutex locks, can provide higher concurrency than mutexes when reading data much more frequently than writing data. When a read-write lock is in write mode, the data it protects can be modified safely because only one thread at a time can own a write-mode lock. When it is in read mode, as long as the thread first acquires the read-write lock in read mode, the data protected by the lock can be read by multiple threads that acquire the read mode lock at the same time. In order to avoid long-term occupancy of read mode locks, generally if a thread tries to acquire the lock in write mode, the read-write lock usually blocks subsequent read mode lock requests.
    Like mutexes, read-write locks must be initialized before use and destroyed before their underlying memory is freed.
#include <pthread.h>
int pthread_rwlock_init( pthread_rwlock_t *restrict rwlock,
                         const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock *rwlock);
                          /* The return values ​​of the two functions: if successful, return 0; otherwise, return the error number */

    The attr parameter is the attribute of the read-write lock, and it is NULL to use the default attribute. The PTHREAD_RWLOCK_INITIALIZER constant is also defined in the XSI extension to initialize statically allocated read-write locks.
    You can call pthread_rwlock_rdlock and pthread_rwlock_wrlock to lock the read-write lock in read mode and write mode, respectively. Both of these methods can call pthread_rwlock_unlock to unlock.
#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);
                        /* Three function return values: if successful, return 0; otherwise, return error number */

    Various implementations may limit the number of read-write locks that can be acquired in shared mode, so the return value of pthread_rwlock_rdlock should be checked.
    The Single UNIX Specification also defines conditional versions of the read-write lock primitive and read-write lock functions with timeouts.
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
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);
                        /* All functions return value: if successful, return 0; otherwise, return error number */

    The first two functions return 0 when the lock can be acquired, otherwise, they return the error EBUSY. The latter two functions return an ETIMEDOUT error if the lock cannot be acquired by the time specified.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326095036&siteId=291194637