天天看点

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

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

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

实验描述

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

要求:

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]);

}

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;

}