1. 程式人生 > >UNIX程式設計實驗六 執行緒及其同步—哲學家問題

UNIX程式設計實驗六 執行緒及其同步—哲學家問題

                                                   實驗六 執行緒及其同步—哲學家問題

學習執行緒的程式設計和同步。

二 實驗要求:

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[n];

   sem_t sem[n];

   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”時,等待執行緒中只有一個能夠對訊號量做減法並繼續執行,另一個還將處於等待狀態。