Try   HackMD

2018q1 Homework (quiz13)

tags: linyunwen raygoah

contributed by <LinYunWen, raygoah>

測驗 1

考慮以下 C 程式:

在 macOS 編譯並執行,得到以下程式輸出:

Main thread create threads Thread 1 create successfully thread1 : I'm thread 1 thread1 : number = 0 Thread 2 create successfully thread2 : I'm thread 2 thread2 : number = 0 pid-90400 Main thread wait for threads pid-0 Main thread wait for threads pid - 0 Thread 1 Finished pid - 0 Thread 2 Finished thread1 : number = 2 thread2 : number = 3 thread1 : number = 4 thread2 : number = 5 pid : 90400, thread1 : done pid - 90400 Thread 1 Finished pid : 90400, thread2 : done pid - 90400 Thread 2 Finished

留意第 65 行 pid = fork(); 敘述,fork 系統呼叫觸發後,會「分叉」執行單元為原本的 parent process 和新建立的 child process。從以下選項中挑出最符合的描述。

  • 我們先看到 main function 裡面,先做了 mutex 的初始化,並且在 #67 呼叫 thread_create 來建立 threads

    ​​​​int main() { ​​​​ pthread_mutex_init(&mut, NULL); ​​​​ printf("Main thread create threads\n"); ​​​​ thread_create(); ​​​​ pid = fork(); ​​​​ printf("pid-%d Main thread wait for threads\n", pid); ​​​​ thread_wait(); ​​​​ return 0; ​​​​}
  • 而在建立的過程中,會去執行 thread1、thread2 function,這邊可以注意到的是,因為先建立 thread1,因此一定會先 print thread1 : I'm thread 1 再 print thread2 : I'm thread 2

    ​​​​temp = pthread_create(&thread[0], NULL, thread1, NULL)
    ​​​​temp = pthread_create(&thread[1], NULL, thread2, NULL)
  • 當 thread 建立時,他們個別會去呼叫 thread1, thread2,而這兩個 function ,幾乎是一樣,都是在一個 for loop 中,利用 mutex lock 對 number 這個變數做加一,然後 thread1 停 2s , thread2 停 3s

    ​​​​static void *thread1() { ​​​​ printf("thread1 : I'm thread 1\n"); ​​​​ for (i = 0; i < MAX; i++) { ​​​​ printf("thread1 : number = %d\n", number); ​​​​ pthread_mutex_lock(&mut); ​​​​ number++; ​​​​ pthread_mutex_unlock(&mut); ​​​​ sleep(2); ​​​​ } ​​​​ printf("pid : %d, thread1 : done\n", pid); ​​​​ pthread_exit(NULL); ​​​​}
  • 最重要的地方在於 #10 ,變數 number 和 i 都是全域的變數,而這個 number 是 for loop 中要輸出的變數, i 是 for loop 中的計數變數

    ​​​​static int number = 0, i;
  • 首先說明當 i < MAX 時,(MAX = 5),for loop 會繼續做下去,所以最重點是,你可以看到 thread1, thread2 是交互輸出,並且基本上應該會輸出共 5 次,因為計數用的 i 是共用的,所以兩者的輸出總次數應該不超過 5 次。那為什麼輸出結果會有六次呢?這要回顧一開始的輸出 #5, #7,當 #5 完成時,thread1 會有兩種可能,正在執行 mutex 內,或是 做完 mutex 內 已進入 sleep(2),但是不管如何這時的 i = 0 ,結果 thread2 就要開始執行,因此他進入 for loop 時 i 也還是 0 ,才會有共輸出六次這樣的狀況

  • 接下來,對於上面同樣程式碼,在同樣環境下,執行結果卻不同的情況,要對 #7 thread2 的輸出做一下說明,剛剛提到過,在開始執行 thread2 時,可能遇到兩種狀況,thread1 正在執行 mutex 內的部份,或是做完 mutex 內的部份了,已經進入 sleep(2)。而這兩者的差別在於,因為 number 是全域變數,因此如果是狀況一,此時的 number = 0,因此 thread2 輸出 thread2 : number = 0,反之,若是狀況二, number 已經加一,故輸出 thread2 : number = 1

thread2
thread1
main
fork
do other
End
do loop
do loop
create
Start
End
join
  • 接著在 ubuntu 16.04 測試看看
    • 在 ubuntu 16.04 的環境下編譯:
    ​​​​$ gcc -pthread -o 13 13.c
    
    • 在 ubuntu 16.04 的環境下執行
      • 第一次得到的結果如下:
      ​​​​​​​​Main thread create threads ​​​​​​​​Thread 1 create successfully ​​​​​​​​thread1 : I'm thread 1 ​​​​​​​​Thread 2 create successfully ​​​​​​​​thread1 : number = 0 ​​​​​​​​thread2 : I'm thread 2 ​​​​​​​​thread2 : number = 1 ​​​​​​​​pid-4888 Main thread wait for threads ​​​​​​​​pid-0 Main thread wait for threads ​​​​​​​​pid - 0 Thread 1 Finished ​​​​​​​​pid - 0 Thread 2 Finished ​​​​​​​​thread1 : number = 2 ​​​​​​​​thread2 : number = 3 ​​​​​​​​thread1 : number = 4 ​​​​​​​​thread2 : number = 5 ​​​​​​​​pid : 4888, thread1 : done ​​​​​​​​pid - 4888 Thread 1 Finished ​​​​​​​​pid : 4888, thread2 : done ​​​​​​​​pid - 4888 Thread 2 Finished
      • 而多試幾次後,發現到,得到了和第一次執行結果不同的情形,如下:
      ​​​​​​​​Main thread create threads ​​​​​​​​Thread 1 create successfully ​​​​​​​​thread1 : I'm thread 1 ​​​​​​​​thread2 : I'm thread 2 ​​​​​​​​thread2 : number = 0 ​​​​​​​​thread1 : number = 0 ​​​​​​​​Thread 2 create successfully ​​​​​​​​pid-16051 Main thread wait for threads ​​​​​​​​pid-0 Main thread wait for threads ​​​​​​​​pid - 0 Thread 1 Finished ​​​​​​​​pid - 0 Thread 2 Finished ​​​​​​​​thread1 : number = 2 ​​​​​​​​thread2 : number = 3 ​​​​​​​​thread1 : number = 4 ​​​​​​​​thread2 : number = 5 ​​​​​​​​pid : 16051, thread1 : done ​​​​​​​​pid - 16051 Thread 1 Finished ​​​​​​​​pid : 16051, thread2 : done ​​​​​​​​pid - 16051 Thread 2 Finished
    • 輸出結果不同主要是在 #5 到 #7 中

    • 這時會看到,因為當 #5 完成時,thread1 會有兩種可能,正在執行 mutex 的部份,或是已經結束 mutex 的部份,進入到 sleep(2),而這兩種可能的差異,確實造成實際執行程式後得到的結果會有所不同

延伸題目:

  1. 解釋程式運作,擴充測試的範圍
  2. 參照 Threads and fork(): think twice before mixing them,添增 pthread_atfork() 來改寫程式碼,使得 child/parent 兩個 process 的執行緒得以交替輸出

參考資料