线程同步

线程同步

进程/线程同步:竞争关系 协作关系

1
2
3
4
5
int pthread_exit(void *result);
result:线程结束时需要记录的数据
int pthread_join(pehread_t id,void **result,);//会阻塞,等待id指定的线程结束
result:获取pthread_exit方法传递的数据
id:指定等待或者获取的id

互斥量(互斥锁)

锁:加锁,解锁

线程在进入临界区之前,加锁操作(如果锁是加锁状态的,则执行加锁操作将被阻塞)。线程在退出临界区之后,解锁状态。

1
2
3
4
5
6
7
互斥锁类型:pthread_mutex_t 一般将锁定义到全局
互斥锁操作:
初始化:int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr);
加锁:int pthread_mutex_lock(pthread_mutex_t *mutex);//阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);//非阻塞
解锁:int pthread_mutex_unlock(pehread_mutex_t *mutex);
销毁:int pthread_mutex_destroy(pthread_mutex_t *mutex);

example:

主线程负责接收用户输入,函数线程负责将用户输入打印到终端界面。

数据传递:主线程到函数线程–>全局空间

主线程和函数线程需要同步:

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
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
#include<time.h>
pthread_mutex_t mutex;
char buff[128]={0};
void *fun(void *arg)
{
while(1)
{
pthread_mutex_lock(&mutex);
if(strncmp(buff,"end",3)==0)
{
break;
}
printf("fun:%s\n",buff);
memset(buff,0,127);
int n=rand()%3;
sleep(n);
pthread_mutex_unlock(&mutex);
}
}
int main()
{
srand((unsigned int)(time(NULL)*time(NULL)));
pthread_mutex_init(&mutex,NULL);//初始化的锁时解锁状态的
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);
while(1)
{
pthread_mutex_lock(&mutex);
printf("input: ");
fgets(buff,127,stdin);
pthread_mutex_unlock(&mutex);
if(strncmp(buff,"end",3)==0)
{
break;
}
int n=rand()%3+1;
sleep(n);
}
pthread_join(id,NULL);
pthread_mutex_destroy(&mutex);
exit(0);
}

信号量–线程库中的信号量

特殊的一个计数器

信号量类型:全局 sem_t类型

1
2
3
4
5
6
7
初始化:int sem_init(sem_t *sem,int shared,int val);
shared:设定是否可以在进程间共用
val:信号量的初始值
P操作:int sem_wait(sem_t *sem);
如果信号量的值为0,则P操作阻塞
V操作:int sem_post(sem_t *sem);
销毁:int sem_destroy(sem_t *sem);

example:完成上面互斥锁那个操作,使用两个信号量。

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
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<pthread.h>
#include<time.h>
#include<semaphore.h>
sem_t sem1;
sem_t sem2;
char buff[128]={0};
void *fun()
{
while(1)
{
sem_wait(&sem1);
if(strncmp(buff,"end",2)==0)
{
break;
}
printf("fun:%s",buff);
memset(buff,0,128);
sem_post(&sem2);//函数线程唤醒主线程
}
}
int main()
{
sem_init(&sem1,0,0);
sem_init(&sem2,0,1);
pthread_t id;
int res=pthread_create(&id,NULL,fun,NULL);
assert(res==0);

while(1)
{
sem_wait(&sem2);
printf("input: ");
fgets(buff,127,stdin);
sem_post(&sem1);//主线程唤醒函数线程
if(strncmp(buff,"end",2)==0)
{
break;
}
}
pthread_join(id,NULL);
sem_destroy(&sem1);
sem_destroy(&sem2);
exit(0);
}

条件变量

1、mutex加锁

2、wait调用,将线程放到cond现成列表中,然后解锁

3、wait返回时,mutex又会被锁住

4、对mutex解锁

传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放在等待条件的线程列表上,然后对互斥量解锁,这两个操作是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。

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

#include <pthread.h>

char buff[128] = {0};

pthread_cond_t cond; // 条件变量
pthread_mutex_t mutex; // 互斥锁

// thread1 -->arg
// thread2 -->arg
void *fun(void *arg)
{
char *s = (char*)arg;

while(1)
{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex); // wait是会阻塞的,直到有线程对cond进行signal或者broadcast
pthread_mutex_unlock(&mutex);

printf("%s: %s", s, buff);

if(strncmp(buff, "end", 3) == 0)
{
break;
}

}
}

int main()
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);

pthread_t id[2];
int res = pthread_create(&id[0], NULL, fun, "thread1");
assert(res == 0);
res = pthread_create(&id[1], NULL, fun, "thread2");
assert(res == 0);

while(1)
{
printf("input: ");
fgets(buff, 127, stdin);
if(strncmp(buff, "end", 3) == 0) // 唤醒在条件变量cond上等待的所有线程
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);

break;
}
else // 唤醒在条件变量cond上等待的一个线程
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}

pthread_join(id[0], NULL);
pthread_join(id[1], NULL);

pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);

exit(0);
}

读写锁:

读加锁:允许多个线程同时进行读加锁(所有线程都只是执行读操作),在互斥锁的基础上做一些扩充,可以允许多个线程以读的操作方式去占据锁,使得线程执行有更高的并行性。