UNIX程序设计实验六 线程及其同步—哲学家问题
2012-06-16 21:04
337 查看
实验六 线程及其同步—哲学家问题
学习线程的编程和同步。
二 实验要求:
1、程序语法
philosopher_th <N> [ -t <time> ]
N是哲学家的个数(N >= 2)。time是哲学家进餐和沉思的持续时间值,缺省为2秒。
2、哲学家的编号为0 ~ N-1,分别用N个线程独立模拟。
3、程序的输出要简洁,例如,当编号为3的哲学家在进餐时,就打印:
philosopher 3 iseating
而当他在沉思时,则打印:
philosopher 3 isthinking
不要输出其他任何信息。
4、使用pthread的semaphore.h提供的信号量同步线程。
5、程序一直运行,直到人为地终止它(如按Ctrl-C或Ctrl-\)。不允许出现僵尸进程。
四实验程序
#include"apue.h"
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
intmain(int argc,char * argv[])
{
unsigned int n,time;
int i;
if(argc==2)
{
n=(unsigned int)atoi(argv[1]);
time=2;
printf("** The default time is: %d **\n",time);
printf("** The number of philosopher is %d **\n",n);
}
elseif(argc==4&&(strcmp(argv[2],"-t"))==0)
{
n=(unsigned int)atoi(argv[1]);
time=(unsigned int)atoi(argv[3]);
printf("** The design time is:%d **\n",time);
printf("** The number of philosopher is %d **\n",n);
}
else
{
printf("Usage:./philosopher N -ttime\n");
}
pthread_t tid
;
sem_t sem
;
for(i=0;i<n;i++)
{ interr;
err=sem_init(&sem[i],0,1);
if(err!=0)
printf("semaphore[i] initfailed\n");
}
void thinking(int i,int time)
{
printf("philosopher %d is thinking \n",i);
sleep(time);
}
void eating(int i,int time)
{
printf("philosopher %d is eating \n",i);
sleep(time);
}
void takefork(int i)
{
if(i==n-1)
{
sem_wait(&sem[0]);
sem_wait(&sem[i]);
}
else
{
sem_wait(&sem[i]);
sem_wait(&sem[i+1]);
}
}
void putfork(int i)
{
if(i==n-1)
{
sem_post(&sem[0]);
sem_post(&sem[i]);
}
else
{
sem_post(&sem[i]);
sem_post(&sem[i+1]);
}
}
void *thr_fn(void * arg)
{
int i=(int) arg;
sleep(i);
while(1)
{
thinking(i,time);
takefork(i);
eating(i,time);
putfork(i);
}
}
for(i=0;i<n;i++)
{
int err;
err=pthread_create(&tid[i],NULL,thr_fn,(void*)i);
if(err!=0)
printf("can't create thread%d:%s\n",i,strerror(err));
}
while(1)
pause;
}
四实验结果
源程序名:thread_philosopher.c
可执行程序名:thph
编译方法:gccthread_philosopher.c error2e.c –o thph -lpthread
结束方法:ctrl+c
运行过程:
1.编译
2.实现第一种情况:(自己定义哲学家的个数,并且使用默认时间值)
例如:./thph 5
3.实现第二种情况:(自己定义哲学家个数和使用时间值)
例如:./thph 5 -t 3
五 实验小结
通过本次实验,可以学到:信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:
extern intsem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。
函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
函数sem_destroy(sem_t *sem)用来释放信号量sem。
信号量用sem_init函数创建的,下面是它的说明:
#include<semaphore.h>
int sem_init (sem_t *sem, intpshared, unsigned int value);
这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。pshared参数控制着信号量的类型。如果 pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。我们现在只对不让进程共享的信号量感兴趣。 (这个参数受版本影响), pshared传递一个非零将会使函数调用失败。
这两个函数控制着信号量的值,它们的定义如下所示:
#include <semaphore.h>
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);
这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。
sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,介信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。
学习线程的编程和同步。
二 实验要求:
1、程序语法
philosopher_th <N> [ -t <time> ]
N是哲学家的个数(N >= 2)。time是哲学家进餐和沉思的持续时间值,缺省为2秒。
2、哲学家的编号为0 ~ N-1,分别用N个线程独立模拟。
3、程序的输出要简洁,例如,当编号为3的哲学家在进餐时,就打印:
philosopher 3 iseating
而当他在沉思时,则打印:
philosopher 3 isthinking
不要输出其他任何信息。
4、使用pthread的semaphore.h提供的信号量同步线程。
5、程序一直运行,直到人为地终止它(如按Ctrl-C或Ctrl-\)。不允许出现僵尸进程。
四实验程序
#include"apue.h"
#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
intmain(int argc,char * argv[])
{
unsigned int n,time;
int i;
if(argc==2)
{
n=(unsigned int)atoi(argv[1]);
time=2;
printf("** The default time is: %d **\n",time);
printf("** The number of philosopher is %d **\n",n);
}
elseif(argc==4&&(strcmp(argv[2],"-t"))==0)
{
n=(unsigned int)atoi(argv[1]);
time=(unsigned int)atoi(argv[3]);
printf("** The design time is:%d **\n",time);
printf("** The number of philosopher is %d **\n",n);
}
else
{
printf("Usage:./philosopher N -ttime\n");
}
pthread_t tid
;
sem_t sem
;
for(i=0;i<n;i++)
{ interr;
err=sem_init(&sem[i],0,1);
if(err!=0)
printf("semaphore[i] initfailed\n");
}
void thinking(int i,int time)
{
printf("philosopher %d is thinking \n",i);
sleep(time);
}
void eating(int i,int time)
{
printf("philosopher %d is eating \n",i);
sleep(time);
}
void takefork(int i)
{
if(i==n-1)
{
sem_wait(&sem[0]);
sem_wait(&sem[i]);
}
else
{
sem_wait(&sem[i]);
sem_wait(&sem[i+1]);
}
}
void putfork(int i)
{
if(i==n-1)
{
sem_post(&sem[0]);
sem_post(&sem[i]);
}
else
{
sem_post(&sem[i]);
sem_post(&sem[i+1]);
}
}
void *thr_fn(void * arg)
{
int i=(int) arg;
sleep(i);
while(1)
{
thinking(i,time);
takefork(i);
eating(i,time);
putfork(i);
}
}
for(i=0;i<n;i++)
{
int err;
err=pthread_create(&tid[i],NULL,thr_fn,(void*)i);
if(err!=0)
printf("can't create thread%d:%s\n",i,strerror(err));
}
while(1)
pause;
}
四实验结果
源程序名:thread_philosopher.c
可执行程序名:thph
编译方法:gccthread_philosopher.c error2e.c –o thph -lpthread
结束方法:ctrl+c
运行过程:
1.编译
2.实现第一种情况:(自己定义哲学家的个数,并且使用默认时间值)
例如:./thph 5
3.实现第二种情况:(自己定义哲学家个数和使用时间值)
例如:./thph 5 -t 3
五 实验小结
通过本次实验,可以学到:信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:
extern intsem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。
函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。
函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。
函数sem_destroy(sem_t *sem)用来释放信号量sem。
信号量用sem_init函数创建的,下面是它的说明:
#include<semaphore.h>
int sem_init (sem_t *sem, intpshared, unsigned int value);
这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。pshared参数控制着信号量的类型。如果 pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。我们现在只对不让进程共享的信号量感兴趣。 (这个参数受版本影响), pshared传递一个非零将会使函数调用失败。
这两个函数控制着信号量的值,它们的定义如下所示:
#include <semaphore.h>
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);
这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。
sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,介信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。
相关文章推荐
- 实验6线程及其同步—哲学家问题的线程
- Unix课程设计实验四 编制模拟“五个哲学家”问题的程序(进程同步)
- Java总结(十)—实现Runnable接口创建线程,线程安全同步,死锁(哲学家进餐问题),读写锁
- Unix高级编程:线程基础、线程的创建、退出、分离、汇合、同步问题
- 编制模拟“五个哲学家”问题的线程实现(unix操作系统系)---6
- 线程同步学习_哲学家问题
- UNIX 程序设计实验二 同步与异步write的效率比较
- 进程(线程)间同步互斥经典问题(二)哲学家问题
- 编制模拟“五个哲学家”问题的线程实现(unix操作系统系)---6
- 线程间通信及其安全问题
- 经典生产者与消费者问题(线程的同步与互斥)
- 重庆大学软件学院2013级操作系统实验二:线程及其调度
- UNIX上C++程序设计守则(信号和线程)
- UNIX 编程中错误输出的线程安全问题
- Java线程(八):锁对象Lock-同步问题更完美的处理方式
- Java线程同步卖票问题解决方法
- Java线程安全问题与同步锁
- Linux/Unix 编程中 POSIX 函数的线程安全问题
- Java线程(八):锁对象Lock-同步问题更完美的处理方式
- 操作系统经典同步互斥问题——哲学家就餐