long blogs

进一步有进一步惊喜


  • Home
  • Archive
  • Tags
  •  

© 2026 long

Theme Typography by Makito

Proudly published with Hexo

C-锁

Posted at 2021-07-23 C 

锁的引入是为了解决多线程处理共享资源的存取。Posix Thread提供一套mutex函数来处理互斥锁。互斥锁只有两种状态,上锁/解锁。当一个线程想要上锁一个已经上锁的互斥锁,则该线程就会挂起。A线程上锁了lock,B线程想要再次上锁lock。B线程挂起,直到A线程将lock解锁之后B线程才从挂起状态退出。
锁类型:

  • 自旋锁:spinlock(不停判断锁是否可用,内核里面,效率高,占用cpu资源,用户态不提供)
  • 互斥锁: mutex、信号量(节省CPU资源,效率不高,存在睡眠唤醒)

锁创建/销毁

创建锁

  • 静态方式: 使用PTHREAD_MUTEX_INITIALIZER宏
1
2
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

  • 动态方式: pthread_mutex_init()
    函数定义:
1
int pthread_mutex_init (pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);

__mutexattr用于指定互斥锁属性,NULL为缺省属性,通常使用NULL。

释放锁

1
int pthread_mutex_destroy (pthread_mutex_t *__mutex)

要求锁处于解锁状态,否则返回EBUSY。在Linux中锁不占用任何资源。

锁操作

  • 加锁
1
int pthread_mutex_lock (pthread_mutex_t *__mutex);

所有的锁都不可能被两个线程同时得到。
普通锁: 解锁者可以是同进程的任何线程。同一进程P,可以由A线程加锁一个普通锁,B线程解锁。
检错锁: 只有加锁者能解锁,否则返回EPERM。同一进程P,A线程加锁一个检错锁,B线程解锁该锁失败.
嵌套锁: 文档实现要求加锁者才能解锁,实验结果不一致?

  • 解锁
1
int pthread_mutex_unlock (pthread_mutex_t *__mutex);

快速锁: 解除锁定。
递归锁:使锁上的引用计数-1。
检错锁: 解锁者和加锁者一致才解锁,否则啥也不做。

  • 测试锁
1
int pthread_mutex_trylock (pthread_mutex_t *__mutex);

和pthread_mutex_lock语义相同,锁被占用时不会阻塞挂起,返回EBUSY

锁属性

属性:pthread_mutexattr_t mutexattr;

设置属性函数:pthread_mutexattr_settype(&mutexattr, kind);

类型:

  • PTHREAD_MUTEX_TIMED_NP 默认值,普通锁,快速锁。加锁之后,阻塞等待队列,解锁之后,阻塞队列中的先到的先得,有次序要求。普通锁容易死锁,线程A获得普通锁之后再次调用lock,A线程由于不能获得锁导致A挂起,进而导致死锁。
    创建锁:
1
2
3
4
pthread_mutex_t mutex;
pthread_mutexattr_t mutexattr;
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_TIMED_NP);
pthread_mutex_init(&mutex, &mutexattr);
  • PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁.同一个线程可获得同一个锁多次,通过多次unlock解锁,使用计数机制。不同线程请求,加锁线程解锁时重新竞争,怎么理解,解锁只有公平竞争,没有先后区别。

  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁。同一个线程请求同一个锁,返回EDEADLK。否则和PTHREAD_MUTEX_TIMED_NP动作一致。保证多次加锁不不会死锁。

线程在加锁后解锁前被取消,锁将永远处于锁定状态。需要在退出回调phtead_cleanup_push/pthread_cleanup_pop中解锁。

不应该在信号处理函数中使用互斥锁,容易造成死锁。

读写锁

为了并发读不受影响,使用读写锁进行共享变量的处理。

1
2
3
4
5
6
int pthread_rwlock_init(pthread_rwlock_t *rwlock,const pthread_rwlockattr_t *attr);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

可以多个线程使用读锁,并不会冲突。但是当某个线程想要写锁时,如果其他线程已经获得过读锁,就会进入阻塞状态,等读锁全部释放之后才能上写锁。这时候就像互斥锁了。

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
static char share_data[20] = {"HelloWorld"};
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
void *th(void *arg) {
bool *running = arg;
pthread_t id = pthread_self();
while (*running) {
pthread_rwlock_rdlock(&rwlock);
printf("I locked read lock [%lu]\n", id);
sleep(rand() % 3);
printf("share_data: %s\n", share_data);
pthread_rwlock_unlock(&rwlock);
sleep(1);
printf("I unlock read lock [%lu]\n", id);
}
pthread_exit(NULL);
}

void test_rwlock(){
pthread_t pool[5] = {0};
bool running = true;
for (int i = 0; i < 5; ++i) {
pthread_create(&pool[i], NULL, th, &running);
}
sleep(5);
pthread_t id = pthread_self();
pthread_rwlock_wrlock(&rwlock);
printf("I lock write lock[%lu]\n",id);
memset(share_data, 0, sizeof(share_data));
strcpy(share_data, "WorldHello");
pthread_rwlock_unlock(&rwlock);
printf("I unlock write lock[%lu]\n",id);

sleep(10);
running = false;
for (int i = 0; i < 5; ++i) {
pthread_join(pool[i], NULL);
}
}
int main(int argc, char *argv[]) {
test_rwlock();
}

运行结果:

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
I locked read lock [140487461066496]
I locked read lock [140487452673792]
I locked read lock [140487444281088]
I locked read lock [140487435888384]
I locked read lock [140487427495680]
share_data: HelloWorld
share_data: HelloWorld
share_data: HelloWorld
share_data: HelloWorld
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
share_data: HelloWorld
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
share_data: HelloWorld
share_data: HelloWorld
share_data: HelloWorld
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
share_data: HelloWorld
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
share_data: HelloWorld
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
share_data: HelloWorld
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
share_data: HelloWorld
share_data: HelloWorld
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
share_data: HelloWorld
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
share_data: HelloWorld
share_data: HelloWorld
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
share_data: HelloWorld
share_data: HelloWorld
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
share_data: HelloWorld
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
share_data: HelloWorld
share_data: HelloWorld
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
share_data: HelloWorld
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
share_data: HelloWorld
share_data: HelloWorld
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
share_data: HelloWorld
share_data: HelloWorld
share_data: HelloWorld
share_data: HelloWorld
share_data: HelloWorld
I lock write lock[140487469520704]
I unlock write lock[140487469520704]
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
share_data: WorldHello
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
share_data: WorldHello
share_data: WorldHello
share_data: WorldHello
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
share_data: WorldHello
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
share_data: WorldHello
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
share_data: WorldHello
share_data: WorldHello
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
share_data: WorldHello
share_data: WorldHello
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
share_data: WorldHello
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
share_data: WorldHello
share_data: WorldHello
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
share_data: WorldHello
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
I unlock read lock [140487427495680]
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
I locked read lock [140487427495680]
share_data: WorldHello
share_data: WorldHello
share_data: WorldHello
share_data: WorldHello
share_data: WorldHello
I unlock read lock [140487435888384]
I locked read lock [140487435888384]
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
I unlock read lock [140487452673792]
I locked read lock [140487452673792]
share_data: WorldHello
I unlock read lock [140487444281088]
I locked read lock [140487444281088]
I unlock read lock [140487427495680]
I locked read lock [140487427495680]
I unlock read lock [140487461066496]
I locked read lock [140487461066496]
share_data: WorldHello
share_data: WorldHello
share_data: WorldHello
share_data: WorldHello
I unlock read lock [140487444281088]
I unlock read lock [140487435888384]
I unlock read lock [140487427495680]
I unlock read lock [140487452673792]
share_data: WorldHello
I unlock read lock [140487461066496]

读是可以被并发读的,但是写的话必须等待所有的读锁释放了才能写。

一些锁的测试

//TODO 整理知识点

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
//
// Created by lqx on 2021/7/23.
//
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

void clean_handler(void *arg) {
printf("clean_handler run\n");
pthread_mutex_t *mutex = arg;
pthread_mutex_unlock(mutex);
}

void *th_handler(void *arg) {
pthread_mutex_t *lock = arg;
pthread_mutex_lock(lock);

printf("Thread \n");
for (int i = 0; i < 3; ++i) {
printf("i=%d\n", i);
sleep(1);
}
printf("pthread_cleanup_pop ....\n");

pthread_exit(NULL);
}

void test_1() {
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_t handle;

pthread_cleanup_push(clean_handler, &mutex)
int ret = pthread_create(&handle, NULL, th_handler, &mutex);
if (ret != 0) {
printf("create thread failed[%s].\n",strerror(errno));
exit(1);
}
pthread_cleanup_pop(1);
pthread_join(handle, NULL);
// 重新来一次
int ret = 0;
ret = pthread_create(&handle, NULL, th_handler, &mutex);
if (ret != 0) {
printf("create thread failed[%s].\n",strerror(errno));
exit(1);
}
pthread_join(handle, NULL);
}

void test_2() {
// 双重解锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
printf("x\n");
pthread_mutex_unlock(&mutex);
printf("y\n");
pthread_mutex_unlock(&mutex);
printf("z\n");

pthread_mutex_lock(&mutex);
printf(":");
pthread_mutex_unlock(&mutex);
}

int main(int argc, char *argv[]) {
test_1();
// test_2();
}

可重入锁(递归锁)

递归锁的初始化核心代码

1
2
3
4
5
6
// 初始化递归锁属性
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);

// 初始化递归锁
pthread_mutex_init(&mutex, &mutex_attr);

测试代码

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
static pthread_mutex_t rec_lock;
static pthread_mutexattr_t rec_lock_attr;


void recursive_function(int count) {
pthread_mutex_lock(&rec_lock);
printf("Lock acquired, count %d\n", count);
if (count > 0)
{
recursive_function(count - 1);
}
pthread_mutex_unlock(&rec_lock);
printf("Lock release,count %d\n", count);
}

void test_recursive_lock() {
//
pthread_mutexattr_init(&rec_lock_attr);
pthread_mutexattr_settype(&rec_lock_attr, PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(&rec_lock, &rec_lock_attr);

recursive_function(3);

pthread_mutex_destroy(&rec_lock);
pthread_mutexattr_destroy(&rec_lock_attr);
}

输出结果:

1
2
3
4
5
6
7
8
Lock acquired, count 3
Lock acquired, count 2
Lock acquired, count 1
Lock acquired, count 0
Lock release,count 0
Lock release,count 1
Lock release,count 2
Lock release,count 3

一种不当使用锁的代码写法

1
2
3
4
5
6
7
while(xxxx) {
pthread_mutex_lock(&lock);
// ....
usleep(10 * 1000);
// ....
pthread_mutex_unlock(&lock);
}

锁会一直跑频繁抢着,无法释放的。

Share 

 Previous post: 重启wsl Next post: iot网络协议 

© 2026 long

Theme Typography by Makito

Proudly published with Hexo