实验六 Linux进程编程,实验六 线程及其同步—哲学家问题的线程版_Unix环境高级编程...

76b5ba58fb8e05095d640dfdbd045795.png

实验六哲学家问题的线程版,本科生跟研究生的实验内容是一样的,但要求不一样。本科生要求用信号量来实现,比较简单,研究生用的是条件变量和互斥量来实现。

实验描述

目的:学习线程的编程和同步。

要求:

1、程序语法

philosopher_th [ -t ]

N是哲学家的个数(N >= 2)。time是哲学家进餐和沉思的持续时间值,缺省为2秒。

2、哲学家的编号为0 ~ N-1,分别用N个线程独立模拟。

3、程序的输出要简洁,例如,当编号为3的哲学家在进餐时,就打印:

philosopher 3 is eating

而当他在沉思时,则打印:

philosopher 3 is thinking

不要输出其他任何信息。

4、使用pthread的semaphore.h提供的信号量同步线程。(本科生要求)

4、仅使用一个条件变量以及一个与其捆绑的锁来同步线程。(研究生要求)

5、程序一直运行,直到人为地终止它(如按Ctrl-C或Ctrl-\)。不允许出现僵尸进程。

本科生实验思路

用semaphore.h提供的信号量来解决的话,这个实验就跟实验四几乎一样了,就只是同步机制不一样而已,可以说,只要替换下相应的同步函数(如用 sem_wait() 替代 lock() ),再稍做修改,这实验就完成了…

所以并没用什么可以讲的,注意下这个实验是可以指定哲学家的数量,以及注意线程的最大限制数量就行了。《实验指导》里是这么说的:注意,Linux系统对线程的处理与进程类似,因此,你能创建的线程数目与进程数目的总和,不能大于系统对你的限制,具体说就是20个。

完整代码

编译时需加上参数 -lpthread ,即:

gcc philosopher_th.c error2e.c -lpthread -o philosopher_th

参数 -lpthread 在使用 pthread 线程包的系统时是必须的。

#include "apue.h"

#include

#include

static sem_t *forks; // 信号量表示叉子状态

static N; // 哲学家的个数

static int nsecs = 2;

/* 显示时间,方便查看结果,提交时应删除 */

static char now[80];

char* getTime() {

time_t tim;

struct tm *at;

time(&tim);

at=localtime(&tim);

strftime(now, 79, "%H:%M:%S", at);

return now;

}

/*

* 拿叉子的定义

* 如果哲学家中同时存在左撇子和右撇子,则哲学家问题有解

*/

void takeFork(int i) {

if(i == N-1) { // 人为设定第N-1位哲学家是右撇子

sem_wait(&forks[0]);

sem_wait(&forks[i]);

} else { // 其他是左撇子

sem_wait(&forks[i]);

sem_wait(&forks[i+1]);

}

}

/* 放下叉子 */

void putFork(int i) {

if(i == N-1) {

sem_post(&forks[0]);

sem_post(&forks[i]);

}

else {

sem_post(&forks[i]);

sem_post(&forks[i+1]);

}

}

void thinking(int i, int nsecs) {

printf("philosopher %d is thinking\t%s\n", i, getTime());

sleep(nsecs);

}

void eating(int i, int nsecs) {

printf("philosopher %d is eating\t\t%s\n", i, getTime());

sleep(nsecs);

}

/* 哲学家行为 */

void* philosopher(void *n) {

int i = (int)n;

while(1) {

thinking(i, nsecs); // 哲学家i思考nsecs秒

takeFork(i); // 哲学家i拿起叉子

eating(i, nsecs); // 哲学家i进餐nsecs秒

putFork(i); // 哲学家i放下叉子

}

}

int main(int argc, char * argv[]) {

int i;

pthread_t tpid;

int status;

/* 处理输入 */

if ( (argc != 2 && argc != 4) || (argc == 4 && (strcmp(argv[2], "-t")) != 0)) {

err_quit("useage: philosopher_th [ -t ]");

}

N = atoi(argv[1]);

if (N < 2 || N > 20) {

err_quit("N is the number of philosopher, N >= 2 && N <= 20");

}

if (argc == 4) {

nsecs = atoi(argv[3]);

}

/* 分配N个信号量 */

forks = (sem_t *) malloc( N * sizeof(sem_t) );

/* 初始化信号量 */

for( i = 0; i < N; i++) {

sem_init(forks + i, 0, 1);

}

for(i = 0; i < N; i++) {

status = pthread_create(&tpid, NULL, philosopher, (void *) i);

if( status < 0 ) {

err_sys("pthread_create fail.");

}

}

pthread_join(tpid, NULL); /* 我们对线程的返回值不感兴趣,

但是若不等待子线程的结束,则主线程的结束将导致整个进程的结束 */

}

研究生实验思路

与实验四类似,但使用的是线程同步,可以通过使用条件变量和一个互斥量来实现。同时以分离状态来创建线程,让线程终止时可以自动释放资源。

哲学家的行为可以用如下函数描述:

void* philosopher(void * arg) {

int i = (int)arg;

while(1) {

thinking(i, nsecs);

pthread_mutex_lock(&pLock);

while(!isAvailable(i)) {

pthread_cond_wait(&pWait, &pLock);

}

takeFork(i);

pthread_mutex_unlock(&pLock);

eating(i, nsecs);

putFork(i);

pthread_cond_broadcast(&pWait); // 放下叉子后,唤醒等待的线程

}

}

先锁住互斥量,然后尝试获得叉子,如果不能获得叉子,则进入等待。此时pthread_cond_wait会自动释放互斥量的锁。而有哲学家拿起叉子,且进餐结束放下叉子后,通过pthread_cond_broadcast来唤醒等待的进程再次尝试获取叉子。

并且,使用条件变量+互斥量的话,可以简单的以数组int *pFork来表示叉子(值为1表示叉子可用),并且不必考虑左、右撇子的问题,哲学家要么拿起两把叉子,要么不拿叉子。在互斥锁的保护下,这个操作不会有竞争。

拿起叉子的代码如下:

void takeFork(int i) {

pFork[i] = 0;

pFork[(i+1) % N] = 0;

}

完整代码

编译时需加上参数 -lpthread ,即:

gcc philosopher_th.c error2e.c -lpthread -o philosopher_th

参数 -lpthread 在使用 pthread 线程包的系统时是必须的。

#include "apue.h"

#include

static pthread_cond_t pWait = PTHREAD_COND_INITIALIZER;

static pthread_mutex_t pLock = PTHREAD_MUTEX_INITIALIZER;

int N = 0;

int nsecs = 2;

int *pFork; // 01 array presents the forks; 1 -> available

int isAvailable(int i) {

if (pFork[i] && pFork[(i+1) % N])

return 1;

else

return 0;

}

void takeFork(int i) {

pFork[i] = 0;

pFork[(i+1) % N] = 0;

}

void putFork(int i) {

pFork[i] = 1;

pFork[(i+1) % N] = 1;

}

void thinking(int i, int nsecs) {

printf("philosopher %d is thinking\n", i);

sleep(nsecs);

}

void eating(int i, int nsecs) {

printf("philosopher %d is eating\n", i);

sleep(nsecs);

}

void* philosopher(void * arg) {

int i = (int)arg;

while(1) {

thinking(i, nsecs);

pthread_mutex_lock(&pLock);

while(!isAvailable(i)) {

// thinking(i, 0);

pthread_cond_wait(&pWait, &pLock);

}

takeFork(i);

pthread_mutex_unlock(&pLock);

eating(i, nsecs);

putFork(i);

pthread_cond_broadcast(&pWait);

}

}

int main(int argc, char *argv[]) {

int i, err;

pthread_t tid[20];

pthread_attr_t attr;

/* 处理输入 */

if ( (argc != 2 && argc != 4) || (argc == 4 && (strcmp(argv[2], "-t")) != 0)) {

err_quit("useage: philosopher_th [ -t ]");

}

N = atoi(argv[1]);

if (N < 2 || N > 20) {

err_quit("N is the number of philosopher, N >= 2 && N <= 20");

}

if (argc == 4) {

nsecs = atoi(argv[3]);

}

/* 初始化 */

pFork = (int *)malloc(N*sizeof(int));

for (i = 0; i < N; i++) {

pFork[i] = 1;

}

err = pthread_attr_init(&attr); // detach

if (err != 0)

err_quit("pthread arr init error");

err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

if (err != 0)

err_quit("set detachstate error");

for (i = 0; i < N; i++) {

err = pthread_create(&tid[i], &attr, philosopher, (void *)(long int)i);

if (err != 0)

err_quit("can't create thread: %s\n", strerror(err));

}

pause();

return 0;

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值