进程管理
烂尾中 等待后续填坑更新
为什么要进程管理
进程死锁问题
对于进程死锁,常见的几种算法
- 信号量的应用
利用信号量来描述前驱关系 利用前驱图
一开始S1先给S2,S3压力(a,b)(signal(S3),signal(S3))
S1压力完了后
S2 S3可以开始活动,
S2开始给S4,S5压力(c,d)
S2压力结束 S4 S5可以活动
最后S3,S4,S5同时给S6压力
S6,这个人太懒了,他的的启动需要同时有三个人给压力才行,不打不行,所以在b执行完结束后不能立马去执行
必须同时等待三个人(具有前驱关系的三个人)给压力才能执行(等待wait(S3,S4,S5))
graph TD
S1--> S2
S1--> S3
S2--> S4
S2--> S5
S4--> S6
S5--> S6
S3--> S6
几个经典的问题
1.生产者-消费者问题
用记录型信号量解决生产者-消费者问题 在生产者和消费者之间具有n个公用缓冲区
定义三个信号量: mutex:用于保护共享资源的互斥访问,初始值为 1。 empty:表示空闲缓冲区的数量,初始值为缓冲区的容量。 full:表示已满缓冲区的数量,初始值为 0。
生产者线程执行以下操作: 对 empty 进行 P 操作,表示生产者想要占用一个空闲缓冲区。 对 mutex 进行 P 操作,表示生产者开始访问共享资源。
将数据放入缓冲区。 对 mutex 进行 V 操作,表示生产者结束访问共享资源。 对 full 进行 V 操作,表示已满缓冲区的数量增加 1。
消费者线程执行以下操作: 对 full 进行 P 操作,表示消费者想要从一个已满缓冲区中取出数据。 对 mutex 进行 P 操作,表示消费者开始访问共享资源。
从缓冲区中取出数据。 对 mutex 进行 V 操作,表示消费者结束访问共享资源。 对 empty 进行 V 操作,表示空闲缓冲区的数量增加 1。 ```c #include
#include #include
#define BUFFER_SIZE 10
pthread_mutex_t mutex; sem_t empty, full;
void *producer(void *arg) { int i; for (i = 0; i < 100; i++) { sem_wait(&empty); pthread_mutex_lock(&mutex);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 生产数据
printf("生产者生产数据 %d\n", i);
pthread_mutex_unlock(&mutex);
sem_post(&full); } return NULL; } void *consumer(void *arg) { int i; for (i = 0; i < 100; i++) {
sem_wait(&full);
pthread_mutex_lock(&mutex);
// 消费数据
int data;
printf("消费者消费数据 %d\n", data);
pthread_mutex_unlock(&mutex);
sem_post(&empty); } return NULL; }
int main() { pthread_t producer_tid, consumer_tid;
// 初始化信号量 sem_init(&empty, 0, BUFFER_SIZE); sem_init(&full, 0, 0); pthread_mutex_init(&mutex, NULL);
// 创建生产者和消费者线程 pthread_create(&producer_tid, NULL, producer, NULL); pthread_create(&consumer_tid, NULL, consumer, NULL);
// 等待线程结束 pthread_join(producer_tid, NULL); pthread_join(consumer_tid, NULL);
// 销毁信号量和互斥锁 sem_destroy(&empty); sem_destroy(&full); pthread_mutex_destroy(&mutex);
return 0; }
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
7. 优化?
利用AND型信号量解决
```c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define BUFFER_SIZE 10
pthread_mutex_t mutex;
sem_t buffer;
void *producer(void *arg) {
int i;
for (i = 0; i < 100; i++) {
sem_wait(&buffer);
pthread_mutex_lock(&mutex);
// 生产数据
printf("生产者生产数据 %d\n", i);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *consumer(void *arg) {
int i;
for (i = 0; i < 100; i++) {
sem_wait(&buffer);
pthread_mutex_lock(&mutex);
// 消费数据
int data;
printf("消费者消费数据 %d\n", data);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t producer_tid, consumer_tid;
// 初始化信号量
sem_init(&buffer, 0, BUFFER_SIZE);
pthread_mutex_init(&mutex, NULL);
// 创建生产者和消费者线程
pthread_create(&producer_tid, NULL, producer, NULL);
pthread_create(&consumer_tid, NULL, consumer, NULL);
// 等待线程结束
pthread_join(producer_tid, NULL);
pthread_join(consumer_tid, NULL);
// 销毁信号量和互斥锁
sem_destroy(&buffer);
pthread_mutex_destroy(&mutex);
return 0;
}
buffer AND 型信号量表示缓冲区的状态,如果值为 BUFFER_SIZE,表示所有缓冲区都空闲,如果值为 0,表示所有缓冲区都已满。 生产者线程执行流程
生产者想要生产数据时,需要对 buffer 进行 P 操作,表示占用一个空闲缓冲区。只有当缓冲区有空闲空间时,P 操作才会成功。 生产者生产数据后,需要对 buffer 进行 V 操作,表示释放一个空闲缓冲区。 消费者线程执行流程
消费者想要消费数据时,需要对 buffer 进行 P 操作,表示占用一个已满缓冲区。只有当缓冲区有已满数据时,P 操作才会成功。 消费者消费数据后,需要对 buffer 进行 V 操作,表示释放一个已满缓冲区。 优化效果
使用 AND 型信号量可以优化生产者-消费者问题,提高效率。因为:
生产者和消费者可以同时尝试操作缓冲区,而不是互斥操作。 只有当缓冲区有空闲空间时,生产者才能生产数据,避免了生产者生产数据后等待空闲空间的情况。 只有当缓冲区有已满数据时,消费者才能消费数据,避免了消费者消费数据后等待已满数据的情况。
2.哲学家进餐问题
- 定义一个信号量 mutex,用于保护餐桌的互斥访问,初始值为 1。
- 定义一个信号量数组 forks,用于表示每个叉子的状态,初始值为 1。
- 哲学家拿起左边的叉子: 对 mutex 进行 P 操作,表示哲学家想要开始进餐。 对 forks[i] 进行 P 操作,表示哲学家想要拿起左边的叉子。 对 mutex 进行 V 操作,表示哲学家已经拿起左边的叉子。
- 哲学家拿起右边的叉子: 对 forks[i + 1] 进行 P 操作,表示哲学家想要拿起右边的叉子。
- 哲学家吃完饭: 对 forks[i] 进行 V 操作,表示哲学家放下左边的叉子。 对 forks[i + 1] 进行 V 操作,表示哲学家放下右边的叉子。 ```c #include
#include #include
#define NUM_PHILOSOPHERS 5
pthread_mutex_t mutex; sem_t forks[NUM_PHILOSOPHERS];
void *philosopher(void *arg) { int i = (int)arg; while (1) { // 思考 printf(“哲学家 %d 正在思考\n”, i);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 拿起左边的叉子
pthread_mutex_lock(&mutex);
sem_wait(&forks[i]);
pthread_mutex_unlock(&mutex);
// 拿起右边的叉子
sem_wait(&forks[(i + 1) % NUM_PHILOSOPHERS]);
// 进餐
printf("哲学家 %d 正在进餐\n", i);
// 放下右边的叉子
sem_post(&forks[(i + 1) % NUM_PHILOSOPHERS]);
// 放下左边的叉子
sem_post(&forks[i]); } return NULL; }
int main() { pthread_t philosopher_tids[NUM_PHILOSOPHERS];
// 初始化信号量 pthread_mutex_init(&mutex, NULL); for (int i = 0; i < NUM_PHILOSOPHERS; i++) { sem_init(&forks[i], 0, 1); }
// 创建哲学家线程 for (int i = 0; i < NUM_PHILOSOPHERS; i++) { pthread_create(&philosopher_tids[i], NULL, philosopher, (void *)i); }
// 等待线程结束 for (int i = 0; i < NUM_PHILOSOPHERS; i++) { pthread_join(philosopher_tids[i], NULL); }
// 销毁信号量和互斥锁 pthread_mutex_destroy(&mutex); for (int i = 0; i < NUM_PHILOSOPHERS; i++) { sem_destroy(&forks[i]); }
return 0; }
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
AND型信号量优化
```c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_PHILOSOPHERS 5
pthread_mutex_t mutex;
sem_t forks;
void *philosopher(void *arg) {
int i = (int)arg;
while (1) {
// 思考
printf("哲学家 %d 正在思考\n", i);
// 拿起所有叉子
pthread_mutex_lock(&mutex);
sem_wait(&forks);
pthread_mutex_unlock(&mutex);
// 进餐
printf("哲学家 %d 正在进餐\n", i);
// 放下所有叉子
sem_post(&forks);
}
return NULL;
}
int main() {
pthread_t philosopher_tids[NUM_PHILOSOPHERS];
// 初始化信号量
pthread_mutex_init(&mutex, NULL);
sem_init(&forks, 0, 1);
// 创建哲学家线程
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
pthread_create(&philosopher_tids[i], NULL, philosopher, (void *)i);
}
// 等待线程结束
for (int i = 0; i < NUM_PHILOSOPHERS; i++) {
pthread_join(philosopher_tids[i], NULL);
}
// 销毁信号量和互斥锁
pthread_mutex_destroy(&mutex);
sem_destroy(&forks);
return 0;
}
forks AND 型信号量表示所有叉子的状态,如果值为 1,表示所有叉子都可用,如果值为 0,表示至少有一个叉子不可用。 哲学家进餐流程
哲学家想要开始进餐时,需要对 forks 进行 P 操作,表示占用所有叉子。只有当所有叉子都可用时,P 操作才会成功。 哲学家吃完饭后,需要对 forks 进行 V 操作,表示释放所有叉子。 优化效果
使用 AND 型信号量可以优化哲学家进餐问题,提高效率。因为:
哲学家可以同时尝试拿起所有叉子,而不是逐个拿起。 只有当所有叉子都可用时,哲学家才能开始进餐,避免了哲学家拿着一个叉子等待另一个叉子的情况。
3.读者-写者问题
- 定义两个记录型信号量:
read_sem:表示当前正在读数据的读者数量,初始值为 0。 write_sem:表示当前正在写数据的写者数量,初始值为 0。
- 读者线程执行流程:
读者想要开始读数据时,需要对 read_sem 进行 P 操作,表示增加正在读数据的读者数量。 读者读完数据后,需要对 read_sem 进行 V 操作,表示减少正在读数据的读者数量。
- 写者线程执行流程:
写者想要开始写数据时,需要对 write_sem 进行 P 操作,表示增加正在写数据的写者数量。 写者写完数据后,需要对 write_sem 进行 V 操作,表示减少正在写数据的写者数量。
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
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define BUFFER_SIZE 10
pthread_mutex_t mutex;
sem_t read_sem, write_sem;
void *reader(void *arg) {
int i;
for (i = 0; i < 100; i++) {
sem_wait(&read_sem);
pthread_mutex_lock(&mutex);
// 读数据
printf("读者 %d 正在读数据\n", i);
pthread_mutex_unlock(&mutex);
sem_post(&read_sem);
}
return NULL;
}
void *writer(void *arg) {
int i;
for (i = 0; i < 100; i++) {
sem_wait(&write_sem);
pthread_mutex_lock(&mutex);
// 写数据
printf("写者 %d 正在写数据\n", i);
pthread_mutex_unlock(&mutex);
sem_post(&write_sem);
}
return NULL;
}
int main() {
pthread_t reader_tids[10], writer_tids[5];
// 初始化信号量
sem_init(&read_sem, 0, 0);
sem_init(&write_sem, 0, 1);
pthread_mutex_init(&mutex, NULL);
// 创建读者和写者线程
for (int i = 0; i < 10; i++) {
pthread_create(&reader_tids[i], NULL, reader, NULL);
}
for (int i = 0; i < 5; i++) {
pthread_create(&writer_tids[i], NULL, writer, NULL);
}
// 等待线程结束
for (int i = 0; i < 10; i++) {
pthread_join(reader_tids[i], NULL);
}
for (int i = 0; i < 5; i++) {
pthread_join(writer_tids[i], NULL);
}
// 销毁信号量和互斥锁
sem_destroy(&read_sem);
sem_destroy(&write_sem);
pthread_mutex_destroy(&mutex);
return 0;
}
优化:
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
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t mutex;
sem_t rw_sem;
void *reader(void *arg) {
int i;
for (i = 0; i < 100; i++) {
sem_wait(&rw_sem);
pthread_mutex_lock(&mutex);
// 读数据
printf("读者 %d 正在读数据\n", i);
pthread_mutex_unlock(&mutex);
sem_post(&rw_sem);
}
return NULL;
}
void *writer(void *arg) {
int i;
for (i = 0; i < 100; i++) {
sem_wait(&rw_sem);
pthread_mutex_lock(&mutex);
// 写数据
printf("写者 %d 正在写数据\n", i);
pthread_mutex_unlock(&mutex);
sem_post(&rw_sem);
}
return NULL;
}
int main() {
pthread_t reader_tids[10], writer_tids[5];
// 初始化信号量
sem_init(&rw_sem, 0, 1);
pthread_mutex_init(&mutex, NULL);
// 创建读者和写者线程
for (int i = 0; i < 10; i++) {
pthread_create(&reader_tids[i], NULL, reader, NULL);
}
for (int i = 0; i < 5; i++) {
pthread_create(&writer_tids[i], NULL, writer, NULL);
}
// 等待线程结束
for (int i = 0; i < 10; i++) {
pthread_join(reader_tids[i], NULL);
}
for (int i = 0; i < 5; i++) {
pthread_join(writer_tids[i], NULL);
}
// 销毁信号量和互斥锁
sem_destroy(&rw_sem);
pthread_mutex_destroy(&mutex);
return 0;
}
rw_sem AND 型信号量表示读写操作的状态,如果值为 1,表示所有读写资源都可用,如果值为 0,表示所有读写资源都被占用。 读者线程执行流程
读者想要开始读数据时,需要对 rw_sem 进行 P 操作,表示占用一个读写资源。只有当读写资源可用时,P 操作才会成功。 读者读完数据后,需要对 rw_sem 进行 V 操作,表示释放一个读写资源。 写者线程执行流程
写者想要开始写数据时,需要对 rw_sem 进行 P 操作,表示占用所有读写资源。只有当所有读写资源都可用时,P 操作才会成功。 写者写完数据后,需要对 rw_sem 进行 V 操作,表示释放所有读写资源。