一、用信号量完成四个线程的同步。主程序创建四个线程,其中两个线程负责 从文件读取数据到公共缓冲区,另两个线程从缓冲区读取数据分别做加、乘运算。 运行程序之前先编辑好数据文件 file1.dat 和 file2.dat,分别输入 1 2 3 4 5 6 7 8 9 10 和-1 -2 -3 -4 -5 -6 -7 -8 -9 -10。
(1)多次执行参考程序,观察并记录程序执行结果。
1)gedit file1.dat
2)gedit file2.dat
3)gedit ex4_1_1.c
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<unistd.h> //增加的头文件,否则sleep(1)会报错
#define MAXSTACK 100 //定义最大容量为100
int stack[MAXSTACK][2]; //定义一个二维数组
int size=0; //初始化大小
sem_t s; /*定义信号量*/
/*从文件file1.dat 读取数据,每读一次,信号量加一*/
void RData1(void)
{
FILE *fp=fopen("file1.dat","r"); //打开文件file1.dat
while(!feof(fp)) //未到文件尾时继续执行,否则退出循环
{
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]); //将文件中的数据读入二维数组中
sem_post(&s); //增加信号量的值
++size; //长度加一
}
fclose(fp); //关闭文件
}
/*从文件file2.dat 读取数据,每读一次,信号量加一*/
void RData2(void)
{
FILE *fp=fopen("file2.dat","r"); //打开文件file2.dat
while(!feof(fp)) //未到文件尾时继续执行,否则退出循环
{
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]); //将文件中的数据读入二维数组中
sem_post(&s); //增加信号量的值
++size; //长度加一
}
fclose(fp); //关闭文件
}
/*阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待*/
void CData1(void)
{
while(1)
{
sem_wait(&s); //阻塞当前线程直到信号量 sem 的值大于 0
/*将二维数组中的数据相加*/
printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],stack[size][0]+stack[size][1]);
--size; //长度减一
sleep(1); //暂停执行
}
}
void CData2(void)
{
while(1)
{
sem_wait(&s); //阻塞当前线程直到信号量 sem 的值大于 0
/*将二维数组中的数据相乘*/
printf("Multiply:%d*%d=%d\n",stack[size][0],stack[size][1],stack[size][0]*stack[size][1]);
--size; //长度减一
sleep(1); //暂停执行
}
}
int main(void)
{
pthread_t tid1,tid2,tid3,tid4;
sem_init(&s,0,0); /*信号量初始化*/
/*创建4个线程*/
pthread_create(&tid1,NULL,(void *)RData1,NULL);
pthread_create(&tid2,NULL,(void *)RData2,NULL);
pthread_create(&tid3,NULL,(void *)CData1,NULL);
pthread_create(&tid4,NULL,(void *)CData2,NULL);
/*等待线程结束*/
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);
return 0;
}
4)gcc -o ex4_1_1 ex4_1_1.c -lpthread
5)./ex4_1_1
第一次执行
第二次执行
第三次执行
第四次执行
结果分析:由以上四张结果图可看出一个共性,那就是前四行都是0和0的乘法和加法,顺序都是一模一样的,从第五行开始发生变化。这道题目是从文本文档中读入数据,首先要保证数据保存形式是一样的,否则进行运算的时候可能会出现错误匹配的情况,数据保存不同如下图所示:
第一个文件,数据和数据之间没有空格
第二个文件,数据和数据之间以空格分隔
那么错误执行如下:
上图两个文件数据未匹配好,所以执行结果出现错误
前两个线程读取数据放入公共缓冲区中,后两个线程从阻塞等待缓冲区中读取数据后进行运算。从四次结果中可以看出加法和乘法运算是交错进行的,因为运算过程中线程读取数据后,还没来得及执行,信号量已经又加一了。而且还有一点,两个文件中的数据存放格式相同,但是运算的时候确实乱七八糟的,并没有按照顺序来执行加乘运算,因为再每个线程中,数组长度都可以被改变。
(2)修改程序,以实现题目的要求。提示:参照“生产者-消费 者问题”。
1)gedit ex4_1_2.c
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<unistd.h> //增加的头文件,否则sleep(1)会报错
#define MAXSTACK 100 //定义最大容量为100
int stack[MAXSTACK][2]; //定义一个公共缓冲区
int size=0; //初始化大小
sem_t sem1,sem2,sem3,sem4; /*定义信号量*/
/*从文件file1.dat 读取数据,每读一次,信号量加一*/
void RData1(void)
{
FILE *fp=fopen("file1.dat","r"); //打开文件file1.dat
while(!feof(fp)) //未到文件尾时继续执行,否则退出循环
{
sem_wait(&sem1); //阻塞当前线程直到信号量sem1的值大于0
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]); //将文件中的数据读入二维数组中
size++; //长度加一
sem_post(&sem2); //增加信号量sem2的值
}
fclose(fp); //关闭文件
}
/*从文件file2.dat 读取数据,每读一次,信号量加一*/
void RData2(void)
{
FILE *fp=fopen("file2.dat","r"); //打开文件file2.dat
while(!feof(fp)) //未到文件尾时继续执行,否则退出循环
{
sem_wait(&sem2); //阻塞当前线程直到信号量sem2的值大于0
fscanf(fp,"%d %d",&stack[size][0],&stack[size][1]); //将文件中的数据读入二维数组中
size++; //长度加一
sem_post(&sem3); //增加信号量sem3的值
}
fclose(fp); //关闭文件
}
/*阻塞等待缓冲区有数据,读取数据后,释放空间,继续等待*/
void CData1(void)
{
while(1)
{
sem_wait(&sem3); //阻塞当前线程直到信号量 sem 的值大于 0
size--; //长度减一
/*将二维数组中的数据相加*/
printf("Plus:%d+%d=%d\n",stack[size][0],stack[size][1],stack[size][0]+stack[size][1]);
sem_post(&sem4);
//sleep(1); //暂停执行
}
}
void CData2(void)
{
while(1)
{
sem_wait(&sem4); //阻塞当前线程直到信号量 sem 的值大于 0
size--; //长度减一
/*将二维数组中的数据相乘*/
printf("Multiply:%d*%d=%d\n",stack[size][0],stack[size][1],stack[size][0]*stack[size][1]);
sem_post(&sem1);
//sleep(1); //暂停执行
}
}
int main(void)
{
pthread_t tid1,tid2,tid3,tid4;
sem_init(&sem1,0,1); /*信号量初始化*/
sem_init(&sem2,0,0);
sem_init(&sem3,0,0);
sem_init(&sem4,0,0);
/*创建4个线程*/
pthread_create(&tid1,NULL,(void *)RData1,NULL);
pthread_create(&tid2,NULL,(void *)RData2,NULL);
pthread_create(&tid3,NULL,(void *)CData1,NULL);
pthread_create(&tid4,NULL,(void *)CData2,NULL);
/*等待线程结束*/
pthread_join(tid1,NULL);
//pthread_join(tid2,NULL);
//pthread_join(tid3,NULL);
//pthread_join(tid4,NULL);
return 0;
}
2)gcc -o ex4_1_2 ex4_1_2.c -lpthread
3)./ex4_1_2
结果分析:修改以后,通过分别定义四个信号量,然后sem1使得线程1和2互斥,sem2使得线程2和3互斥,sem3使得线程3和4互斥,sem4使得线程4和1互斥,就这样,不断地进行循环往复,使得每一个信号都能够顺序进行,读取正确的数据顺序,完成正确的运算。
二、有两个线程 T1 和 T2 动作描述如下,x、y、z 为两个线程共享变量。信号量 S1 和 S2 的初值均为 0,编写程序完成下面两个线程 T1,T2 并发执行。画出前驱 图,并标注同步信号量。多次运行程序后,观察 x,y,z 输出的值,对结果进行分 析。
线程 T1:
y=1;
y=y+2;
sem_post(&S1);
z=y+1;
sleep(1);
sem_wait(&S2);
y=z+x;
线程 T2:
x=1;
x=x+1;
sem_wait(&S1);
x=x+y;
sleep(1);
sem_post(&S2);
1)gedit ex4_2.c
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<unistd.h>
sem_t sem1,sem2; /*定义信号量*/
int x,y,z;
void thread_func1(void)
{
y=1;
y=y+2;
sem_post(&sem1); //增加信号量sem1的值
z=y+1;
sleep(1);
printf("thread_func1: x = %d, y = %d, z = %d\n",x,y,z);
sem_wait(&sem2); //阻塞当前线程直到信号量sem2的值大于0
y=z+x;
printf("thread_func1 result: x = %d, y = %d, z = %d\n",x,y,z);
}
void thread_func2(void)
{
x=1;
x=x+1;
sem_wait(&sem1); //阻塞当前线程直到信号量sem1的值大于0
x=x+y;
sleep(1);
printf("thread_func2: x = %d, y = %d, z = %d\n",x,y,z);
sem_post(&sem2); //增加信号量sem2的值
printf("thread_func2 result: x = %d, y = %d, z = %d\n",x,y,z);
}
int main(void)
{
pthread_t tid1,tid2;
sem_init(&sem1,0,0); /*信号量初始化*/
sem_init(&sem2,0,0);
/* 创建2个线程*/
pthread_create(&tid1,NULL,(void *)thread_func1, NULL);
pthread_create(&tid2,NULL,(void *)thread_func2, NULL);
/*等待线程结束*/
pthread_join(tid1,NULL);
//pthread_join(tid2,NULL);
printf("x = %d, y = %d, z = %d\n",x,y,z);
return 0;
}
2)gcc -o ex4_2 ex4_2.c -lpthread
3)./ex4_2
结果分析:由上图可看出程序先是执行了第二个线程功能,再执行实现了第二个线程功能。因为根据实验指导书中给出的两个线程代码,在第一个线程中,先增加了sem1的值,然后进行运算,要等待sem2的值大于0,在第二个线程中,sem1已经增加了值,所以大于0,可以执行运算,而后便可以对sem2增加值,使得sem2大于0,这样就可以执行第一个线程中的运算。