1.函数原型:
// 初始化互斥对象属性
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
// 设置互斥锁范围
int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared);
//获取互斥锁范围
int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr, int *pshared);
// 销毁互斥对象属性
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
函数pthread_mutexattr_init()用该实现定义的所有属性的默认值初始化互斥对象属性attr。初始化一个已经初始化的互斥对象的效果是未定义的。在一个互斥锁属性对象被用于初始化一个或多个互斥锁之后,任何影响属性对象的函数(包括撤销)都不会影响任何先前初始化的互斥锁。pthread_mutexattr_destroy()函数的作用是:销毁互斥对象属性;实际上,对象变成未初始化的。一个实现可能导致pthread_mutexattr_destroy()将attr引用的对象设置为一个无效的值。销毁的互斥对象可以使用pthread_mutexattr_init()重新初始化;在对象被撤销后引用它的结果是未定义的。
互斥锁 变量可以是进程专用的(进程内)变量,也可以是系统范围内的(进程间)变量。要在多个进程中的线程之间共享 互斥锁,可以在 共享内存中创建互斥锁,并将 pshared 属性设置为 PTHREAD_PROCESS_SHARED。 此行为与最初的 Solaris 线程实现中 mutex_init() 中的 USYNC_PROCESS 标志等效。
如果 互斥锁的 pshared 属性设置为 PTHREAD_PROCESS_PRIVATE,则仅有那些由同一个进程创建的线程才能够处理该互斥锁。
// 初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
// 释放互斥锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_init() 函数用attr指定的属性初始化互斥对象引用的互斥对象。如果attr为NULL,则使用默认的互斥锁属性;其效果与传递默认互斥对象的地址相同。成功初始化后,互斥锁的状态变为初始化和解锁。试图初始化一个已经初始化的互斥锁会导致未定义的行为。pthread_mutex_destroy()函数的作用是:销毁被互斥对象引用的互斥对象;互斥对象实际上变成了未初始化的。一个实现可能导致pthread_mutex_destroy()将mutex_引用的对象设置为一个无效的值。销毁的互斥对象可以使用pthread_mutex_init()重新初始化;在对象被撤销后引用它的结果是未定义的。销毁已初始化的互斥锁是安全的。试图销毁锁定的互斥锁会导致未定义的行为。在默认互斥对象属性合适的情况下,可以使用宏PTHREAD_MUTEX_INITIALIZER初始化静态分配的互斥对象。其效果等同于调用pthread_mutex_init(),并将参数attr指定为NULL,但是不执行错误检查。
如果成功,pthread_mutex_init()和pthread_mutex_destroy()函数返回零。否则,将返回错误号来指示错误。如果实现了[EBUSY]和[EINVAL]错误检查,就好像它们在函数处理的一开始就被立即执行,并在修改由互斥指定的互斥的状态之前导致错误返回。
pthread_mutex_init()函数将在以下情况下失败:
// 系统缺乏必要的资源(除了内存)来初始化另一个互斥锁。
(EAGAIN)
// 内存不足,无法初始化互斥锁。
(ENOMEM)
// 调用者没有执行该操作的权限。
(EPERM)
// 实现检测到试图重新初始化互斥锁引用的对象,互斥锁是一个先前初始化但尚未销毁的互斥锁。
(EBUSY)
// 由attr指定的值无效。
(EINVAL)
pthread_mutex_destroy()函数可能在以下情况下失败:
// 当互斥对象被另一个线程锁定或引用(例如,当在pthread_cond_wait()或pthread_cond_timedwait()中使用时),该实现检测到试图销毁互斥对象引用的行为。
(EBUSY)
//互斥指定的值无效。
(EINVAL)
2. 互斥锁属性
// 设置互斥锁属性
int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);
// 获取互斥锁属性
int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type);
互斥锁的属性在创建锁的时候指定,在LinuxThreads实现中仅有一个锁类型属性,不同的锁类型在试图对一个已经被锁定的互斥锁加锁时表现不同。当前(glibc2.2.3,linuxthreads0.9)有四个值可供选择:
* PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
* PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
* PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
* PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
3. 其他锁操作
锁操作主要包括加锁pthread_mutex_lock()、解锁pthread_mutex_unlock()和测试加锁 pthread_mutex_trylock()三个,不论哪种类型的锁,都不可能被两个不同的线程同时得到,而必须等待解锁。对于普通锁和适应锁类型,解锁者可以是同进程内任何线程;而检错锁则必须由加锁者解锁才有效,否则返回EPERM;对于嵌套锁,文档和实现要求必须由加锁者解锁,但实验结果表明并没有这种限制,这个不同目前还没有得到解释。在同一进程中的线程,如果加锁后没有解锁,则任何其他线程都无法再获得锁。
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
pthread_mutex_trylock()语义与pthread_mutex_lock()类似,不同的是在锁已经被占据时返回EBUSY而不是挂起等待。
4. 案例
// write.cpp
/**
* 文件名: test.cpp
* 作 者: 浩然
* 功 能:
* 日 期: Tue 27 Apr 2021 04:33:02 PM CST
**/
#include <stdio.h>
#include <pthread.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
pthread_mutex_t *mutex;
pthread_mutexattr_t attr;
int fd = open("/studio3/mutex_mmap", O_RDWR | O_CREAT, 00777);
if(fd < 0){
printf("open mmap file error...");
}
int fdt = open("/studio3/mutex_mmapt", O_RDWR | O_CREAT, 00777);
if(fdt < 0){
printf("open mmapt file error...");
}
ftruncate(fd,sizeof(pthread_mutex_t ));
ftruncate(fdt,sizeof(int));
mutex = (pthread_mutex_t *)mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE,
MAP_SHARED , fd, 0);
int *p_shared = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED , fdt, 0);
*p_shared = 0;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mutex, &attr);
while(1){
pthread_mutex_lock(mutex);
(*p_shared)++;
printf("parent process value is: %d\n", *p_shared);
sleep(5);
pthread_mutex_unlock(mutex);
sleep(2);
}
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(mutex);
munmap(mutex, sizeof(pthread_mutex_t));
munmap(p_shared, sizeof(int));
close(fd);
close(fdt);
return 0;
}
// read.cpp
/**
* 文件名: test.cpp
* 作 者: 浩然
* 功 能:
* 日 期: Tue 27 Apr 2021 04:33:02 PM CST
**/
#include <stdio.h>
#include <pthread.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
pthread_mutex_t *mutex;
int fd = open("/studio3/mutex_mmap", O_RDWR | O_CREAT, 00777);
if(fd < 0){
printf("open mmap file error...");
}
int fdt = open("/studio3/mutex_mmapt", O_RDWR | O_CREAT, 00777);
if(fdt < 0){
printf("open mmapt file error...");
}
ftruncate(fd,sizeof(pthread_mutex_t));
ftruncate(fdt,sizeof(int));
mutex = (pthread_mutex_t *)mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE,
MAP_SHARED , fd, 0);
int *p_shared = (int *)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED , fdt, 0);
while(1){
pthread_mutex_lock(mutex);
(*p_shared)++;
printf("parent process value is: %d\n", *p_shared);
sleep(1);
pthread_mutex_unlock(mutex);
sleep(2);
}
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(mutex);
munmap(mutex, sizeof(pthread_mutex_t));
munmap(p_shared, sizeof(int));
close(fd);
close(fdt);
return 0;
}