---
tags: linyunwen, raygoah
---
# 2018q1 Homework (quiz13)
###### tags: `linyunwen` `raygoah`
contributed by <`LinYunWen`, `raygoah`>
- [第十三周練習題](https://hackmd.io/s/HJV0SEW1Q)
### 測驗 `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
```clike=64
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```
```clike=42
temp = pthread_create(&thread[0], NULL, thread1, NULL)
```
```clike=46
temp = pthread_create(&thread[1], NULL, thread2, NULL)
```
- 當 thread 建立時,他們個別會去呼叫 thread1, thread2,而這兩個 function ,幾乎是一樣,都是在一個 for loop 中,利用 mutex lock 對 number 這個變數做加一,然後 thread1 停 2s , thread2 停 3s
```clike=12
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 中的計數變數
```clike=10
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```
```mermaid
graph TB
subgraph main
Start --- create
create --- fork
join --- end_main(End)
fork --- join
end
create --> do1(do loop)
subgraph thread1
do1 --> join
end
create --> do2(do loop)
subgraph thread2
do2 --> join
end
subgraph fork
fork --> do3(do other)
do3 --> end_fork(End)
end
```
- 接著在 ubuntu 16.04 測試看看
* 在 ubuntu 16.04 的環境下編譯:
```kernel
$ 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),而這兩種可能的差異,確實造成實際執行程式後得到的結果會有所不同
:::info
延伸題目:
1. 解釋程式運作,擴充測試的範圍
2. 參照 [Threads and fork(): think twice before mixing them](http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them),添增 `pthread_atfork()` 來改寫程式碼,使得 child/parent 兩個 process 的執行緒得以交替輸出
:::
### 參考資料
* [Undefined reference to pthread_create in Linux](https://stackoverflow.com/questions/1662909/undefined-reference-to-pthread-create-in-linux)