1 互斥属性类型
#define __SIZEOF_PTHREAD_MUTEXATTR_T 4
typedef union
{
char __size[__SIZEOF_PTHREAD_MUTEXATTR_T];
int __align;
} pthread_mutexattr_t;
2 初始化与销毁互斥属性
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
初始化与销毁互斥属性对象。
pthread_mutexattr_init
以默认值初始化互斥属性对象。使用一个未初始化的互斥属性对象,将导致不确定性的行为。如果输入NULL
,将使用默认的互斥属性值。pthread_mutexattr_destroy
销毁指定的互斥属性对象。一个已销毁的对象可以被重新初始化。使用一个已销毁的互斥属性对象,将导致不确定的行为。该函数在LinuxThread
环境中的实现不做任何事情。- 一个互斥属性对象被修改或销毁不影响使用该互斥属性对象创建的互斥量
3 互斥属性之锁类型
int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind);
int pthread_mutexattr_getkind_np(const pthread_mutexattr_t *attr, int *kind);
- 已弃用。使用
pthread_mutexattr_settype & pthread_mutexattr_gettype
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind);
在LinuxThread
仅支持一个互斥属性mutex kind
。该值有以下可选值。
-
PTHREAD_MUTEX_FAST_NP
快速互斥。默认值
-
PTHREAD_MUTEX_RECURSIVE_NP
递归互斥
-
PTHREAD_MUTEX_ERRORCHECK_NP
带错误检查的互斥 -
后缀
NP
指示旋转是不可移植的(non-portable
)mutex kind
用于决定在企图加锁一个已经处于锁定状态的的互斥时将如何做。- 如果为
PTHREAD_MUTEX_FAST_NP
将挂起当前线程。 - 如果为
PTHREAD_MUTEX_ERRORCHECK_NP
立即返回,并携带错误码EDEADLK
。 - 如果为
PTHREAD_MUTEX_RECURSIVE_NP
立即返回,并携带成功运行的返回码
- 如果为
4 初始化与销毁互斥
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
pthread_mutex_init
-
如果参数为
NULL
,使用默认属性进行初始化。正常初始化后,互斥的状态变成已初始化且未锁定状态。 -
企图初始化一个已初始化的互斥对象,将导致不确定性行为。
-
宏
PTHREAD_MUTEX_INITIALIZER
亦能完成互斥的初始化,但其不做错误检查。
-
-
pthread_mutex_destroy
-
互斥被销毁后变成一个非法值。一个已销毁的互斥对象可被重新初始化。引用一个已销毁的互斥将出现不可预期的行为。
-
销毁一个未锁定且已初始化的互斥是安全的。企图销毁一个锁定状态或被引用(例如
pthread_cond_timedwait
或pthread_cond_wait
)将导致不确定行为。
-
5 互斥之加锁解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
-
如果互斥锁已被另一个线程锁定,调用线程将阻塞直到互斥锁可用。
-
pthread_mutex_lock
与pthread_mutex_trylock
除加锁一个已经处于锁定状态的互斥时,前者进入阻塞状态,直接返回外,二者没区别。
6 案例:互斥锁的使用
使用互斥修改上一节中出错的样例,使其达到预期的效果。
使用流程
- 在每一个需要互斥的地方都需要先加锁,后解锁。
- 加锁解锁必须成对使用,否则可能造成死锁
- 最小代码块内进行加锁解锁,以提高程序运行效率
-
源码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> pthread_mutex_t mutex; int g_num = 0; void *start_routine_01(void *ptr) { for (size_t i = 0; i < 10000; i++) { // 锁定最小单元 pthread_mutex_lock(&mutex); // 锁定 g_num++; pthread_mutex_unlock(&mutex); // 解锁 } return (void *)NULL; } void *start_routine_02(void *ptr) { for (size_t i = 0; i < 10000; i++) { // 锁定最小单元 pthread_mutex_lock(&mutex); // 锁定 g_num++; pthread_mutex_unlock(&mutex); // 解锁 } return (void *)NULL; } int main(int argc, char const *argv[]) { pthread_mutexattr_t attr; // 定义互斥属性 pthread_mutexattr_init(&attr); // 初始化互斥属性 pthread_mutex_init(&mutex, &attr); // 初始化互斥 pthread_mutexattr_destroy(&attr); // 销毁互斥属性 pthread_t thread_id_01; pthread_t thread_id_02; pthread_create(&thread_id_01, NULL, start_routine_01, NULL); pthread_create(&thread_id_01, NULL, start_routine_02, NULL); pthread_join(thread_id_01, NULL); pthread_join(thread_id_02, NULL); printf("计算结果为: %d\n", g_num); printf("主线程即将退出...\n"); pthread_mutex_destroy(&mutex); // 销毁互斥 exit(EXIT_SUCCESS); }
-
结果
计算结果为: 20000
主线程即将退出…