天天看點

實驗六 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;

}