`

互斥量与读写锁操作函数

阅读更多
    互斥变量是用 pthread_mutex_t 数据类型表示的。下面几个函数可操作互斥量。
#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);
                         /* 所有函数的返回值:若成功,返回 0;否则,返回错误编号 */

    使用互斥量前必须先对它进行初始化,可把它设置为常量 PTHREAD_MUTEX_INITIALIZER(只适用于静态分配的互斥量),也可以通过 pthread_mutex_init 函数进行初始化。要用默认的属性初始化互斥量,只需把 pthread_mutex_init 的参数 attr 设为 NULL。如果互斥量是动态分配的,则释放内存前需要调用 pthread_mutex_destroy。
    函数 pthread_mutex_lock 和 pthread_mutex_unlock 可分别对互斥量进行加锁和解锁。如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁。如果线程不希望被阻塞,可以使用 pthread_mutex_trylock 尝试对互斥量加锁。若调用 pthread_mutex_trylock 时互斥量未上锁,则该函数就锁住互斥量,然后直接返回 0,否则该函数就会失败,然后返回 EBUSY。
    下面这段代码描述了如何用互斥量来保护某个数据结构。当有多个线程要访问动态分配的对象时,可以在对象中嵌入引用计数,确保在所有使用该对象的线程完成数据访问之前,该对象的内存空间不会被释放。
#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);
	}
}

    为避免线程在获取锁时永久阻塞,可以使用函数 pthread_mutex_timedlock。
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);
                            /* 返回值:若成功,返回 0;否则,返回错误编号 */

    该函数与 pthread_mutex_lock 基本等价,但它允许绑定线程阻塞时间。当达到超时时间时,它不会对互斥量进行加锁,而是返回错误码 ETIMEDOUT。这里的时间参数是一个绝对时间。
    下面这个示例通过对已有的互斥量进行再次加锁(这通常会造成死锁)来演示了如何用 pthread_mutex_timedlock 来避免永久阻塞。
#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);
	pr_time(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(tout);
	pthread_mutex_unlock(&lock);
	if(err != 0)
		printf("can't lock mutex again: %s\n", strerror(err));
	else
		printf("mutex locked again!\n");
	exit(0);
}

    编译后运行:
$ 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

    说完互斥量,接下来再说说读写锁。
    读写锁也称共享互斥锁,它能在读取数据频率远高于写数据时提供比互斥量更高的并发性。当读写锁在写模式下时,它所保护的数据就可以被安全地修改,因为一次只有一个线程可以拥有写模式的锁。而当它在读模式下时,只要线程先获取了读模式下的读写锁,该锁所保护的数据就可以同时被多个获得读模式锁的线程读取。为避免读模式锁长期占用,一般如果有一个线程试图以写模式获取锁时,读写锁通常会阻塞随后的读模式锁请求。
    与互斥量相同,读写锁在使用前必须初始化,在释放其底层内存前必须销毁。
#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);
                          /* 两个函数返回值:若成功,返回 0;否则,返回错误编号 */

    attr 参数是读写锁的属性,为 NULL 表示使用默认属性。XSI 扩展中也定义了 PTHREAD_RWLOCK_INITIALIZER 常量,用来对静态分配的读写锁进行初始化。
    可以调用 pthread_rwlock_rdlock 和 pthread_rwlock_wrlock 分别在读模式和写模式下锁定读写锁,这两种方式都可以调用 pthread_rwlock_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);
                        /* 三个函数返回值:若成功,返回 0;否则,返回错误编号 */

    各种实现可能会对共享模式下可获取的读写锁的次数进行限制,所以应该检查 pthread_rwlock_rdlock 的返回值。
    Single UNIX Specification 还定义了读写锁原语的条件版本和带有超时的读写锁函数。
#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);
                        /* 所有函数返回值:若成功,返回 0;否则,返回错误编号 */

    可以获取锁时,前两个函数返回 0,否则,它们返回错误 EBUSY。后两个函数如果在到达指定的时刻时还不能获取锁就将返回 ETIMEDOUT 错误。
分享到:
评论

相关推荐

    WINDOWS读写锁实现

    `CRITICAL_SECTION`是Windows API提供的一种互斥量实现,用于保护临界区,确保同一时刻只有一个线程可以执行临界区内的代码。它基于内核对象,并且具有递归特性,即同一线程可以多次进入同一`CRITICAL_SECTION`而...

    linux写优先的读写锁设计

    在 Linux 操作系统中,有两种基本机制来实现数据互斥,即信号量(semaphore)和自旋锁(spinlock)。本文将讨论一种特殊的自旋锁变种,即读写锁(read-write lock),它允许多个进程同时访问共享资源,但是写操作...

    GO语言并发编程之互斥锁、读写锁详解

    在本节,我们对Go语言所提供的与锁有关的API进行说明。这包括了互斥锁和读写锁。我们在第6章描述过互斥锁,但却没有提到过读写锁。这两种锁对于传统的并发程序来说都是非常常用和重要的。 一、互斥锁 互斥锁是传统的...

    写优先防止读线程饿死的读写锁

    文件名`RWLock`很可能是一个实现了上述功能的类或者头文件,其中包含了读写锁的相关函数和数据结构,如`lock_read()`、`unlock_read()`、`lock_write()`和`unlock_write()`等方法,以及用于管理线程等待队列的数据...

    标准C写的线程池和读写锁

    在IT领域,线程池和读写锁是两种重要的多线程编程技术,尤其是在Linux操作系统中。本项目提供了一个标准C语言实现的线程池,同时整合了读写锁的实现,这对于提升程序的并发性能和资源管理具有重要意义。 线程池是一...

    多进程互斥读写XML文件

    - **互斥锁+文件映射**:使用互斥量控制对内存映射文件的访问,可以提高效率,但需要管理好映射区域。 - **原子操作**:某些操作系统的原生函数(如flock())支持原子的文件锁操作,确保操作的完整性。 - **XML库...

    线程间同步机制 读写锁通信机制 线程与信号

    在Linux高级程序设计中,主要介绍了三种线程同步机制:互斥锁、条件变量和读写锁,以及线程与信号的交互。 1. **互斥锁通信机制**: 互斥锁是用于保护临界区的一种机制,确保同一时间只有一个线程能访问共享资源。...

    C语言信号量同步与互斥生产者消费者互斥锁读写者问题哲学家就餐问题课程设计

    C语言信号量同步与互斥生产者消费者互斥锁读写者问题哲学家就餐问题课程设计 本课程设计旨在通过C语言编程来解决信号量机制下的同步、互斥、生产者消费者问题、哲学家就餐问题和读写者问题。 一、信号量机制 信号...

    zynq的linux驱动8-利用互斥量实现竞争保护

    - 避免在持有互斥量的情况下调用可能睡眠的函数,如`schedule()`、`down()`等,这可能导致其他线程永远无法获取互斥量,从而引发死锁。 - 在中断上下文中不能使用互斥量,因为它们可能会导致自旋锁,而中断上下文...

    线程同步的四种详细使用方法--临界区、互斥量、事件等

    互斥量可以用 CreateMutex()函数创建,并用 WaitForSingleObject()函数等待互斥量的释放。 事件(Event)是第三种常用的线程同步方法,它是一种同步对象,可以用来同步多个线程的执行。事件可以用 CreateEvent...

    行业分类-设备装置-读写锁控制方法及装置.zip

    读写锁的主要特点是它的分离性:读操作之间是互斥的,但多个读操作可以并行执行;写操作具有独占性,即任何时候只能有一个线程进行写操作。这种设计提高了系统资源的利用率,因为多个读取者可以同时读取数据,而不会...

    linux无亲缘关系间进程同步通信实现(互斥锁+条件变量+共享内存模式)

    它与互斥锁配合使用,可以在等待某个条件时,释放锁并进入休眠状态,条件改变时,由其他进程唤醒。在C语言中,`pthread_cond_t`表示条件变量,`pthread_cond_init`初始化,`pthread_cond_wait`等待,`pthread_cond_...

    linux无亲缘关系进程间通信(互斥锁+条件变量+共享内存)

    它与互斥锁配合使用,可以在等待特定事件发生时释放锁,避免资源浪费。在Linux中,条件变量由`pthread_cond_t`表示,`pthread_cond_init`初始化,`pthread_cond_wait`等待,`pthread_cond_signal`或`pthread_cond_...

    共享内存+互斥量实现linux进程间通信.pdf

    第二步:对互斥量进程上锁,使用 pthread_mutex_lock() 函数,函数原型如下: ```c int pthread_mutex_lock(pthread_mutex_t *mutex); ``` 函数成功返回 0,错误返回非 0 的错误代码。 第三步:pthread_mutex_...

    操作系统读写者

    操作系统是计算机系统的核心组成部分,它负责管理系统的硬件资源和软件资源,为用户提供一个友好的交互环境。...在实际操作系统设计中,类似的问题还会涉及更复杂的同步策略,例如信号量和读写锁,以适应更复杂的需求。

    Linux操作系统编程(linux多线程的互斥与同步控制及实践)

    总的来说,Linux多线程编程涉及的概念和实践技巧非常广泛,包含了线程的创建与管理、互斥锁、读写锁、条件变量、死锁预防、线程间通信、信号量等重要知识点。在设计和实现多线程程序时,需要合理地选择和使用这些...

    iOS线程读写安全

    在iOS中,Objective-C和Swift都提供了多种机制来实现线程安全,例如锁(Locks)、信号量(Semaphores)、互斥锁(Mutexes)、读写锁(Read-Write Locks)以及GCD的队列同步等。 读写锁是一种优化的线程同步机制,...

    3.8 线程同步1

    总的来说,线程同步是多线程编程中必不可少的一部分,它通过互斥量、读写锁等工具来维护并发执行的线程之间的协调,确保数据的正确性和一致性。在设计多线程程序时,合理地使用这些同步机制可以有效地避免数据竞争和...

    操作系统实践windows文件读写示例

    Windows提供了临界区、互斥量、事件等同步机制,确保文件操作的线程安全。 9. **缓冲区管理**:在读写大量数据时,通常会用到缓冲区来提高效率。了解如何有效地使用缓冲区并管理内存,可以显著提升文件操作性能。 ...

    操作系统实验二互斥机制

    本实验“操作系统实验二:互斥机制”将通过C++编程在Nachos系统上实现三种常见的互斥手段:信号量机制、锁机制以及条件变量机制。 **信号量机制**: 信号量(Semaphore)是一种经典的同步工具,由Dijkstra提出,...

Global site tag (gtag.js) - Google Analytics