线程安全

类型:互斥锁 读写锁 自旋锁

死锁:产生的4个必要条件

锁的实现:忙等待(中断的关闭与启用) 非忙等待(test-set)

线程安全

线程安全就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。那么就说这些线程是安全的。

1、对线程同步,保证同一时刻只有一个线程访问临界资源。

2、在多线程使用线程安全的函数(可重入函数),所谓线程安全的函数指的是:如果一个函数能被多个线程同时调用且不发生竟态条件,则我们称他是线程安全的。

看这样一个代码

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
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

void *fun(void *arg)
{
char buff[]="1 2 3 4 5 6 7 8";
char *p=strtok(buff," ");//并不能保证线程安全
while(p)
{
printf("fun:%s\n",p);
p=strtok(NULL," ");
sleep(1);
}
}
int main()
{
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);

char str[]="a b c d e f g h";
char *p=strtok(str," ");
while(p)
{
printf("main:%s\n",p);
p=strtok(NULL," ");
sleep(1);
}
pthread_exit(0);
}

他的结果是这样的,并没有出现预想的结果,不符合线程安全的前提。

原因是strtok这个方法用的是全局栈区的地址,应该改成局部栈区的地址。

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
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

void *fun(void *arg)
{
char buff[]="1 2 3 4 5 6 7 8";
char *q=NULL;
char *p=strtok_r(buff," ",&q);
while(p)
{
printf("fun:%s\n",p);
p=strtok_r(NULL," ",&q);
sleep(1);
}
}
int main()
{
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);

char str[]="a b c d e f g h";
char *q=NULL;
char *p=strtok_r(str," ",&q);
while(p)
{
printf("main:%s\n",p);
p=strtok_r(NULL," ",&q);
sleep(1);
}
pthread_exit(0);
}

不能保证线程安全的函数

线程与fork

多线程中有一个线程调用fork创建子进程,子进程中会有几个线程?

写个代码测试一下

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
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

void *fun(void *arg)
{
pid_t pid=fork();
assert(pid!=-1);

if(pid==0)
{
printf("i am child,my pid=%d\n",getpid());
}
else
{
printf("i am father and fun,my pid=%d\n",getpid());
}
}
int main()
{
printf("father pid=%d\n",getpid());
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);

sleep(1);
int i=0;
for(;i<5;i++)
{
printf("i am main,my pid=%d\n",getpid());
sleep(1);
}
}

总结:在多线程中一个线程调用fork创建子进程,创建的子进程中只有调用fork的线程被启动,其他线程并没有执行。

线程与锁

多线程创建子进程,并且在创建之前有对互斥锁加锁,子进程中锁的状态如何?

改下上面的代码

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
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex;
void *fun(void *arg)
{
sleep(1);
pid_t pid=fork();//调用fork时,加锁状态
assert(pid!=-1);

if(pid==0)
{
printf("i am child,my pid=%d\n",getpid());
pthread_mutex_lock(&mutex);//此次加锁会被阻塞,死锁
printf("child lock success\n");
sleep(2);
pthread_mutex_unlock(&mutex);
printf("child unlock success\n");
}
else
{
printf("i am father and fun,my pid=%d\n",getpid());
pthread_mutex_lock(&mutex);
printf("father lock success\n");
sleep(2);
pthread_mutex_unlock(&mutex);
printf("father unlock success\n");
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
printf("father pid=%d\n",getpid());
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);

pthread_mutex_lock(&mutex);
int i=0;
for(;i<5;i++)
{
printf("i am main,my pid=%d\n",getpid());
sleep(1);
}
pthread_mutex_unlock(&mutex);
printf("main unlock success\n");
pthread_exit(NULL);
}

没有出现子进程的加锁和解锁,所以在子进程出现了死锁现象,子进程会继承父进程的锁的状态。

那怎么解决死锁问题呢

1
2
3
4
5
6
7
线程库
注册方法
会将fork的执行延后到所有的锁都解锁后,并被prepare方法加锁成功之后
int pthread_atfork(void (*prepare)(void),void (*parent)(void),void (*child)(void));
prepare:对所有的锁执行加锁操作,fork之前调用,保证fork执行过程中,所有的锁都是加锁状态的。
parent:对所有的锁进行解锁,fork之后父进程环境中调用
child:对所有的锁进行解锁,fork之后子进程环境调用

改进代码

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
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex;
void prepare()
{
pthread_mutex_lock(&mutex);
}
void parent()
{
pthread_mutex_unlock(&mutex);
}
void child()
{
pthread_mutex_unlock(&mutex);
}
void *fun(void *arg)
{
sleep(1);
pid_t pid=fork();//调用fork时,加锁状态
assert(pid!=-1);

if(pid==0)
{
printf("i am child,my pid=%d\n",getpid());
pthread_mutex_lock(&mutex);
printf("child lock success\n");
sleep(2);
pthread_mutex_unlock(&mutex);
printf("child unlock success\n");
}
else
{
printf("i am father and fun,my pid=%d\n",getpid());
pthread_mutex_lock(&mutex);
printf("father lock success\n");
sleep(2);
pthread_mutex_unlock(&mutex);
printf("father unlock success\n");
}
}
int main()
{
pthread_mutex_init(&mutex,NULL);
pthread_atfork(prepare,parent,child);
printf("father pid=%d\n",getpid());
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);

pthread_mutex_lock(&mutex);
int i=0;
for(;i<5;i++)
{
printf("i am main,my pid=%d\n",getpid());
sleep(1);
}
pthread_mutex_unlock(&mutex);
printf("main unlock success\n");
pthread_exit(NULL);
}