C 互斥锁 mutex
函数初始化
1
2
3
4
5
6
7
|
#include <pthread.h>
/**
* pthread_mutex_init 使用指定的attr属性初始化一个互斥锁mutex 。
* 如果 atrr 设为 NULL 或者使用一个默认的 pthread_mutexattr_t 类型都是使用默认属性进行初始化。
* 重复初始化一个已经初始化过的锁会导致未知行为。
*/
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
|
除了使用 pthread_mutex_init 函数对 mutex 进行初始化,还可以使用特定的宏在声明 mutex 的时候直接赋值进行静态初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <pthread.h>
// 普通mutex
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
// 可重复加锁
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
// 有错误检查的mutex,同一线程重复加锁报错
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
// 上面那个带不带NP后缀取决于系统,我用的Ubuntu18.04对应的宏为PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP。
|
去初始化
与初始化对应的为去初始化,去初始化之后的锁可以再次进行初始化操作。
1
2
3
4
5
|
/**
* 可以销毁一个初始化过的锁。使用此函数销毁一个mutex,可以再次初始化。
* 如果尝试销毁一个锁定状态的mutex会导致未知行为。
*/
int pthread_mutex_destroy(pthread_mutex_t *mutex);
|
加锁
1
2
3
4
5
6
7
8
|
// 普通加锁,重复加锁会阻塞进程
int pthread_mutex_lock (pthread_mutex_t *__mutex);
/**
* 重复加锁返回异常
*/
int pthread_mutex_trylock (pthread_mutex_t *__mutex);
// 带有超时功能加锁
int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout);
|
pthread_mutex_lock 对一个 mutex 加锁。如果一个线程试图锁定一个已经被另一个线程锁定的互斥锁,那么该线程将被挂起,直到拥有该互斥锁的线程先解锁该互斥锁。
默认的 mutex 为不可重入锁,如果定义 mutex 为 PTHREAD_MUTEX_RECURSIVE 类型,则为可重入锁,可以对其进行多次加锁。
解锁
1
2
|
// 解锁
int pthread_mutex_unlock (pthread_mutex_t *__mutex);
|
代码测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;/*初始化互斥锁*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//init cond
void *thread1(void*);
void *thread2(void*);
int i = 1; //global
int main(void){
pthread_t t_a;
pthread_t t_b;//two thread
pthread_create(&t_a,NULL,thread2,(void*)NULL);
pthread_create(&t_b,NULL,thread1,(void*)NULL);//Create thread
printf("t_a:0x%x, t_b:0x%x: \n", t_a, t_b);
pthread_join(t_b,NULL);//wait a_b thread end
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
void *thread1(void *junk){
sleep(3); // 等待让2先获取到锁
pthread_mutex_lock(&mutex); //互斥锁,会等待2释放锁
printf("thread1 lock\n");
sleep(2);
pthread_cond_signal(&cond); // 发送信号唤醒2,但锁仍然在自己这
printf("thread1 send signal\n");
sleep(2);
pthread_mutex_unlock(&mutex);
printf("thread1 end\n");
return 0;
}
void *thread2(void*junk){
pthread_mutex_lock(&mutex); // 获取锁
printf("thread2 lock\n");
sleep(5);
pthread_cond_wait(&cond,&mutex); // 释放锁,等待信号唤醒
printf("thread2 wait end \n");
sleep(1);
pthread_mutex_unlock(&mutex);
printf("thread2 end\n");
return 0;
}
|
1
2
3
4
5
6
7
|
t_a:0x3720000, t_b:0x37a3000:
thread2 lock
thread1 lock
thread1 send signal
thread1 end
thread2 wait end
Program ended with exit code: 0
|
读写锁
读写锁,顾名思义用在读写的地方,读写的地方要求就是如果是写的话只能一个线程拥有,防止写错覆盖新的值。
如果是读状态可以多个线程拥有,这样就提高了效率,读写锁用于对数据结构读的次数远大于写的情况。
读写锁可以设置为两种加锁状态,即读锁定和写锁定状态。
- 当处于写锁定状态时,所有加锁操作都会被阻塞.
- 当处于读锁定状态时,所有试图设置读锁定都会成功,所有试图设置写锁定都会被阻塞,并且还会阻塞后续所有的读锁定加锁操作,直到所有的读锁定都被解锁。
初始化
1
2
3
4
5
6
|
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
// 使用常量进行初始化
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;
|
锁去初始化
1
2
|
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
|
加锁
读状态的锁
1
2
3
|
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
// 不阻塞,成功则返回0
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
|
写状态的锁
1
2
3
|
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
// 不阻塞,成功则返回0
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
|
解锁
解锁不区分读写,统一使用这个解锁
1
|
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
|
代码例子
读线程1先启动,再启动线程2写,当线程2请求写权限后,验证线程3是否能够正常去读
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
#include<pthread.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;
void *thread1(void*);
void *thread2(void*);
void *thread3(void*);
int i = 1; //global
int main(void){
pthread_t t_a;
pthread_t t_b;//two thread
pthread_t t_c;//two thread
pthread_create(&t_a,NULL,thread1,(void*)NULL);
pthread_create(&t_b,NULL,thread2,(void*)NULL);
pthread_create(&t_c,NULL,thread3,(void*)NULL);
pthread_join(t_c,NULL);//wait a_b thread end
pthread_rwlock_destroy(&rwlock);
exit(0);
}
void *thread3(void* junk){
// 暂停2秒,等待线程2先去请求锁
sleep(2);
printf("thread3 request lock\n");
pthread_rwlock_rdlock(&rwlock);
printf("thread3 lock\n");
sleep(2);
printf("thread3 sleep end\n");
pthread_rwlock_unlock(&rwlock);
printf("thread3 end\n");
return 0;
}
void *thread1(void *junk){
pthread_rwlock_rdlock(&rwlock);
printf("thread1 lock\n");
sleep(5);
printf("thread1 sleep end\n");
pthread_rwlock_unlock(&rwlock);
printf("thread1 end\n");
return 0;
}
void *thread2(void*junk){
printf("thread2 request lock\n");
pthread_rwlock_wrlock(&rwlock);
printf("thread2 lock\n");
sleep(2);
printf("thread2 sleep end\n");
pthread_rwlock_unlock(&rwlock);
printf("thread2 end\n");
return 0;
}
|
有线程请求写之后,后续读线程不能再获取到锁,得等待写线程释放锁之后才能继续读。
1
2
3
4
5
6
7
8
9
10
11
12
|
thread1 lock
thread2 request lock
thread3 request lock
thread1 sleep end
thread1 end
thread2 lock
thread2 sleep end
thread2 end
thread3 lock
thread3 sleep end
thread3 end
Program ended with exit code: 0
|