1. 在Linux下创建一对父子进程,实现wait等待
创建子进程的fork()
函数位于unistd.h
头文件中,等待进程运行结束的waitpid()
函数位于sys/wait.h
头文件中。当成功创建子进程时,fork()
函数对父进程返回该子进程的进程号,对子进程返回0;若创建失败则会返回-1。编写程序如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(){
int status;
pid_t child = fork();
if(child < 0){
printf("Sorry, Generate child fail!\n");
exit(0);
}
else if(child == 0) printf("Hi, YigeLIU! This is the child process!\n");
else{
printf("Hi, YigeLIU! This is the father process!\n");
waitpid(child, &status, 0);
printf("END WAIT\n");
}
return 0;
}
对该程序进行测试运行,结果如下:
2. 在 Linux下利用信号机制实现进程间通信
子进程主体结构为一个死循环,当父进程发出kill信号时杀死子进程,同时需要注意的是,此时父进程不能直接结束,而应等待子进程被回收,以避免僵尸进程的产生。父进程杀死子进程时,子进程打印退出的信息。两者的通讯通过函数“kill()”与函数“signal()”实现。程序中使用sleep()来放缓程序执行,使得过程展现得更清晰。具体代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
void exit_output(){
printf("Bye, father!\n");
exit(0);
}
int main(){
pid_t child = fork();
if(child < 0){
printf("Generate child process failed!\n");
exit(0);
}
else if(child == 0){
signal(SIGUSR1, exit_output);
while(1){
printf("This is the child process!\n");
sleep(1);
}
}
else{
sleep(5);
kill(child, SIGUSR1);
printf("Father : Child process has killed!\n");
wait(NULL); //等待子进程资源回收,防止僵尸进程出现
}
return 0;
}
对程序进行测试,结果如下:
3. 在 Linux下创建2个线程A和B并循环输出数据或字符串
笔者选取1~5进行输出,线程A在屏幕上用while循环顺序递增地输出1~5的自然数;线程B在屏幕上用while循环顺序递减地输出5~1之间的自然数。为避免输出太快,每隔1秒输出一个数。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void *myprint_a(){
for(int i = 1; i <= 5; ++i){
printf("A : %d\n", i);
sleep(1);
}
return ;
}
void *myprint_b(){
for(int i = 5; i > 0; --i){
printf("B : %d\n", i);
sleep(1);
}
return ;
}
int main(){
pthread_t tid_a;
int status_a = pthread_create(&tid_a, NULL, myprint_a, NULL);
if(status_a != 0){
printf("Generate thread A failed!\n");
exit(0);
}
pthread_t tid_b;
int status_b = pthread_create(&tid_b, NULL, myprint_b, NULL);
if(status_b != 0){
printf("Generate thread B failed!\n");
exit(0);
}
pthread_join(tid_a, NULL);
pthread_join(tid_b, NULL);
return 0;
}
编译(需要添加pthread链接库,因此需要加上编译选项-lpthread
)并运行该程序,如下:
4. 在 Linux下利用线程实现“生产者-消费者”同步控制
首先创建一个共享的存储区,其类型为int型,大小为32。创建两个信号灯“s_blank”和“s_full”分别用来表示该存储区是否为空和是否为满,两者的初始值分别为32和0。分别创建3个生产者和消费者进程。
接着完成生产者Consumer()与消费者Producer()两个函数。对于生产者函数,首先对信号灯s_blank进行p操作占用一个存储区资源,接着拿到对资源区写的权限,得到后更新存储区中内容,释放该权限,然后对信号灯s_full进行v操作,使得消费者可以访问该资源。对于消费者函数,首先对信号灯s_full进行p操作以获取访问存储区资源的权限,接着拿到对资源区读的权限,得到后读取存储区内容,释放该权限,然后对信号灯s_blank进行v操作,使得生产者可以再度占用该存储区资源以便数据的写入。
利用cnt来记录每个生产者的生产量和每个消费者的消费量,主进程等待1秒钟让生产者和消费者队列进行操作,之后修改flag值使得所有生产者和消费者退出线程。此时输出相关生产量和消费量。代码中在cnt[i]值自增前注释掉了一样输出对应线程对应操作数的代码,在测试时,可以清除展示每个线程的工作信息。这里为了防止信息输出过多而将其注释掉。
具体代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
int shared_block[32];
sem_t s_blank, s_full; //信号灯声明
int step_p = 0, step_c = 0; //访问的数据区位置
pthread_mutex_t mutex; //资源区是否被占用的锁
int cnt_p[3], cnt_c[3]; //记录各生产者和消费者的生产量和消费量
int flag = 1; //进程退出条件
void *Producer(int id){
while(flag){
sem_wait(&s_blank); //p操作
if(pthread_mutex_trylock(&mutex) != 0){ //当无法占用资源区时
sem_post(&s_blank);
continue;
}
int data = rand() % 369;
shared_block[step_p] = data;
step_p = (step_p + 1) % 32;
//printf("Producer %d : %d\n", id, data);
//sleep(1);
cnt_p[id]++;
pthread_mutex_unlock(&mutex);
sem_post(&s_full); //v操作
}
pthread_exit(0);
}
void *Consumer(int id){
while(flag){
sem_wait(&s_full);
if(pthread_mutex_trylock(&mutex) != 0){ //当无法占用资源区时
sem_post(&s_full);
continue;
}
int data = shared_block[step_c];
step_c = (step_c + 1) % 32;
//printf("Consumer %d : %d\n", id, data);
//sleep(1);
cnt_c[id]++;
pthread_mutex_unlock(&mutex);
sem_post(&s_blank);
}
pthread_exit(0);
}
int main(){
//初始化信号灯
sem_init(&s_blank, 0, 32); //0表明空间被进程内的线程所共享;空间大小为32
sem_init(&s_full, 0, 0);
//创建线程
printf("HAVE STATED, PLEASE WAIT\n");
pthread_t producer[3], consumer[3];
for(int i = 0; i < 3; ++i){
pthread_create(&producer[i], NULL, Producer, i);
pthread_create(&consumer[i], NULL, Consumer, i);
}
sleep(1); flag = 0;
printf("------THE RESULT------\n");
for(int i = 0; i < 3; ++i){
printf("Producer %d produce %d products\n", i, cnt_p[i]);
printf("Consumer %d consume %d products\n", i, cnt_c[i]);
}
//销毁信号灯
sem_destroy(&s_blank);
sem_destroy(&s_full);
pthread_mutex_destroy(&mutex);
return 0;
}
编译并运行该程序,观察到生产者与消费者形成同步,如下:
5. 在Linux下利用线程模拟哲学家就餐问题并提供死锁和非死锁解法
非死锁
给5根筷子分别申请一个锁,用来表示该筷子是否被占用。给5位哲学家每人创建一个线程,用来模拟其取筷子的过程。这5个线程进行进行等待操作,以模拟实际情况。
利用函数deal()来处理每个哲学家遇到的情况。首先根据哲学家所坐位置来确定其左手边和右手边筷子的编号;然后进入循环,哲学家先拿起左手边的筷子,然后尝试拿起右手边的筷子,若成功拿起,则开始吃饭;若右手边没有筷子,则放下左手刚拿起的筷子并进入下一次循环。
具体代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t chopsticks[6]; //5个筷子的互斥锁,0不用,使用1~5号
void deal(char ch){
int left, right; //左右筷子的编号,i号筷子在i号哲学家左侧;哲学家逆时针坐
if(ch == 'A') { left = 1; right = 5; }
else if(ch == 'B') { left = 2; right = 1; }
else if(ch == 'C') { left = 3; right = 2; }
else if(ch == 'D') { left = 4; right = 3; }
else { left = 5; right = 4; }
while(1){
sleep(1); //思考
pthread_mutex_lock(&chopsticks[left]); //拿起左手的筷子
printf("Left chopstick(pick) : %d\n", left);
if(pthread_mutex_trylock(&chopsticks[right]) != 0){ //若右手边没有筷子
pthread_mutex_unlock(&chopsticks[left]); //放下左手的筷子
printf("Left chopstick(put) : %d\n", left);
continue;
}
//若右手边有筷子
printf("Right chopstick(pick) : %d\n", right);
//吃饭
printf("---EATING TIME FOR : %c---\n", ch);
sleep(1);
//吃完饭
pthread_mutex_unlock(&chopsticks[left]); //放下左手的筷子
printf("Left chopstick(put) : %d\n", left);
pthread_mutex_unlock(&chopsticks[right]); //放下右手的筷子
printf("Right chopstick(put) : %d\n", right);
}
}
int main(){
//初始化锁
for(int i = 0; i < 6; ++i) pthread_mutex_init(&chopsticks[i], NULL);
//创建5个哲学家线程
pthread_t A, B, C, D, E;
pthread_create(&A, NULL, deal, 'A');
pthread_create(&B, NULL, deal, 'B');
pthread_create(&C, NULL, deal, 'C');
pthread_create(&D, NULL, deal, 'D');
pthread_create(&E, NULL, deal, 'E');
//线程同步
pthread_join(A, NULL);
pthread_join(B, NULL);
pthread_join(C, NULL);
pthread_join(D, NULL);
pthread_join(E, NULL);
return 0;
}
编译并运行该程序,得到哲学家吃饭的结果,如下:
死锁
仅需对非死锁情况进行一定的修改即可。当哲学家拿起左手边的筷子时,对右手边的筷子不尝试拿起,而是直接进行资源的申请;同时,当右手边没有筷子可拿时也不放下左手的筷子而是持续等待;这样便会导致死锁的产生。
具体代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
pthread_mutex_t chopsticks[6]; //5个筷子的互斥锁,0不用,使用1~5号
void deal(char ch){
int left, right; //左右筷子的编号,i号筷子在i号哲学家左侧;哲学家逆时针坐
if(ch == 'A') { left = 1; right = 5; }
else if(ch == 'B') { left = 2; right = 1; }
else if(ch == 'C') { left = 3; right = 2; }
else if(ch == 'D') { left = 4; right = 3; }
else { left = 5; right = 4; }
while(1){
sleep(1); //思考
pthread_mutex_lock(&chopsticks[left]); //拿起左手的筷子
printf("Left chopstick(pick) : %d\n", left);
pthread_mutex_lock(&chopsticks[right]); //拿起右手边有筷子
printf("Right chopstick(pick) : %d\n", right);
//吃饭
printf("---EATING TIME FOR : %c---\n", ch);
sleep(1);
//吃完饭
pthread_mutex_unlock(&chopsticks[left]); //放下左手的筷子
printf("Left chopstick(put) : %d\n", left);
pthread_mutex_unlock(&chopsticks[right]); //放下右手的筷子
printf("Right chopstick(put) : %d\n", right);
}
}
int main(){
//初始化锁
for(int i = 0; i < 6; ++i) pthread_mutex_init(&chopsticks[i], NULL);
//创建5个哲学家线程
pthread_t A, B, C, D, E;
pthread_create(&A, NULL, deal, 'A');
pthread_create(&B, NULL, deal, 'B');
pthread_create(&C, NULL, deal, 'C');
pthread_create(&D, NULL, deal, 'D');
pthread_create(&E, NULL, deal, 'E');
//线程同步
pthread_join(A, NULL);
pthread_join(B, NULL);
pthread_join(C, NULL);
pthread_join(D, NULL);
pthread_join(E, NULL);
return 0;
}