---
# System prepended metadata

title: Thread
tags: [研究所, pthreads, thread pool, 計系, linux threads]

---

# Thread
## Thread 特點
- ==thread 有自己的==
	- thread ID
	- <font color = "red">PC</font>
	- <font color = "red">register sets</font>
	- <font color = "red">stack</font>
- ==和其他 thread 共用==
	- text section
	- data section 
	- OS resources 
		- open files
		- flags 

![image](https://hackmd.io/_uploads/SyrbU-hw6.png)

- threads 共享 memory
> thread 共享 code, text section 的好處就是允許一個 app 有很多個 threads 在同個 address space 活動

- <font color = "red">切換 thread 仍然要 context switch （只是比切換 process 快）</font>

---
## Thread Models

常見 Thread Models：
- POSIX $\rightarrow$ Pthreads
- Java $\rightarrow$ Green Threads
> JVM runs as a single process，會去 schedule threads in app
> - kernel 不知道有 multithread
>> 好處是 ==thread switch== efficient （<font color = "red">不用 system call</font>） 
>> 缺點是所有 thread 共享這個 process 分到的 CPU time，所以當 thread 越多，performance 會急遽下降（因為更多的 threads 去共享相同量的 CPU time）
- Solaris, System V UNIX $\rightarrow$ light-weight processes (LWPs)
> - many to many
> - <font color = "red">由 ==user-mode thread library== schedule between LWPs 而不是 kernel</font>

![image](https://hackmd.io/_uploads/B1o93qe5a.png)


### Pthreads

![image](https://hackmd.io/_uploads/Bk1S9Z2Pa.png)

> 重點： <font color = "red"> global 變數 sum 被 threads 共享</font>
> - 如果 thread 有用 ``pthread_attr_init()`` 設 attribute，就可以設定 stack size, scheduling info⋯⋯（每個 thread 有各自的 attr）
> - ``pthread_create()`` 的第三個 parameter（此例為 runner()）就是這個被建立的 thread 要開始執行的地方 
> - 如果 ``pthread_join()`` 是 success call，代表要等的那個 thread 已經 terminate，所以 caller 就可以選擇是否回收資源；如果``pthread_join()`` call 失敗，會產生 zombie thread
>> <font color = "snake">**zombie**</font>: a process that has terminated, but whose parent has not yet called ``wait()`` 


因為 multicore 越來越普遍，所以很常會要寫一個包含很多 threads 的 program，如果一次要等很多 threads，一種寫法：

![image](https://hackmd.io/_uploads/B1WkZf2D6.png)

> 把 ``pthread_join()`` 包在 loop 裡，就能一次等很多 threads terminate

#### Pthread 常考 function 整理
==``pthread_join(pthread_t thread, void **retval)``== 
$\rightarrow$ 等指定的 thread terminate

==``pthread_mutex_lock(pthread_mutex_t *mutex)``==
$\rightarrow$ lock 指定的 mutex
$\rightarrow$ 當 invoke 此 function 時，如果 mutex 已經被別的 thread lock 住，call 這個 function 的 thread 就會被 blocked，直到擁有 mutex 的 thread 發 ``pthread_mutex_unlock()``  讓 mutex 變成 available

==``pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)``==

![image](https://hackmd.io/_uploads/BkLPmI0Pa.png)

> 在 call 完 ``pthread_mutex_lock()``，取得 mutex 後，thread 就會去檢查 condition（此例中的 ``a != b``），如果條件不成立， thread 就會接著 invoke ``pthread_cond_wait()`` ，把 mutex lock 跟 condition variable 當作 parameter pass 過去。
>  call ``pthread_cond_wait()`` 會釋放 mutex，讓其他 thread 可以 access shared data，因此也就可能會修改到 shared data 的值，讓 condition 條件成立。
>  改了 shared data 的 thread 會 invoke ``pthread_cond_broadcast()`` 或是 ``pthread_cond_signal()``，signal 原本因為 ``cond`` 被 blocked 的 thread
>> conditional clause (此例的 ``a != b``) 必需要被包在 loop 裡面，這樣 signal 以後才會去 recheck 條件是否成立


$\rightarrow$ atomically release mutex and cause the calling thread to block on the condition variable ``cond``
$\rightarrow$ 要求 call ``pthread_cond_wait()`` 的 thread 要先用 ``pthread_mutex_lock()`` 取得 mutex，以防 race condition
$\rightarrow$ ``cond``是 threads 共享的 conditional variable，如果有 thread 要對``cond``做更動，需要先獲得 ``mutex`` 


==``pthread_cond_signal(pthread_cond_t *cond)``==

![image](https://hackmd.io/_uploads/HJKTu8RPT.png)


$\rightarrow$ unblock 至少一個因為正在等``cond`` 而被 blocked 的 thread (wake up sleeping or waiting thread)
$\rightarrow$ 如果都沒有 thread 正在因為 ``cond`` 被 blocked，就不做任何事
$\rightarrow$ <font color = "red"> call ``pthread_cond_signal()`` 不會釋放 mutex lock</font>，是在 call ``pthread_cond_signal()`` 後再 call ``pthread_mutex_unlock()`` 才會釋放 mutex
$\rightarrow$ <font color = "red">call 完 ``pthread_cond_signal()``後，call ``pthread_cond_signal()`` 的 thread 不會馬上把 CPU 給被 signal 的 thread</font>，而是要再接著發完 ``pthread_mutex_unlock()``，把 mutex 給被 signal 的 thread，才會把控制權給 call ``pthread_cond_wait()`` 的 thread


==``int sem_post(sem_t *sem)``==
$\rightarrow$ ++unlock++ a semaphore referenced by sem
> 如果 call 完 ``int sem_post()`` 的結果 semaphore 值是正的，代表沒有 thread 正在被 blocked waiting for semaphore to become unlocked，``int sem_post()`` 就單純只將值增加
> 但如果 call 完 ``int sem_post()`` 的結果 semaphore 值是 0，代表其中一個被 blocked waiting 的 thread 會被成功從原本它 call 的 `` sem_wait()`` return
>> 如果有多個 threads 正在被 blocked ，就根據當初 thread 的 attribute 設定，先 return priority 高的，如果有多個 thread 都是最高 priority，再讓等最久的先 

==``int sem_init(sem_t *sem, int pshared, unsigned value)``==
$\rightarrow$ ``value`` 是 semaphore ``sem`` 的值，如果 ``pshared`` 值是 0，代表這個 semaphore 只被同個 process 中的 threads 共享；如果`pshared`` 值 $\not=$ 0，代表這個 semaphore 是 shared between processes


###  Linux Threads

- Linux 沒有區分 Process 和 Thread，而是用 <font color = "snake">task</font> 同時代表 Process 和 Thread

Linux 產生 thread 的 sys call: ``clone()``
> ``clone()`` 被 invoke 的時候會 pass 一系列的 **flags**，來決定 parent 和 child 之間要共享多少東西

![image](https://hackmd.io/_uploads/HJDyYG3wa.png)

如果沒有設任何的 flag，代表 parent 和 child 之間沒有共享任何東西 
$\rightarrow$ 這種情況下，``clone()`` 的效果就和 ``fork()``差不多

---
## Thread Pools

假如我們沒有限制 concurrently active in system 的 thread 個數，可能會耗盡 system resources（如 CPU time, memory），因此其中一個解決這個問題的方法就是 Thread Pool

Thread Pool  的概念就是在 start-up 的時候就預先 create 一些 thread，然後把他們放到 thread pool 中，等之後工作分派給他們

$\rightarrow$ 當 server 收到 request 時，不會直接 create 一個新的 thread，而是把 request 丟到 thread pool ，再去等新的 request 
$\rightarrow$ thread pool 收到 request ，如果剛好有可以用的 thread，就直接 service；但如果沒有 available 的 thread，task 就會被放到 queue 裡，直到有 thread 完成工作

### Thread Pool 好處

- 從現有的 thread 找一個去 service 比收到 request 再 create 一個快
- 會限制一個時間點內的 thread 個數
- 把 create task 和 task perform 分開可以比較有彈性的安排，例如說把 task 排程

Thread Pool 裡要有多少 threads 可以由像是 CPU 數目、Physical Memory 大小、預期的 concurrent client request⋯⋯決定，有些 Thread Pool Architecture 還可以動態調整 Thread Pool 大小

---
### Reference
- 恐龍 p.302-303
- [pthread_join(3) — Linux manual page](https://man7.org/linux/man-pages/man3/pthread_join.3.html)
- [pthread_cond_wait - IBM](https://www.ibm.com/docs/en/zos/2.1.0?topic=functions-pthread-cond-wait-wait-condition-variable)
- [pthread_cond_signal - IBM](https://www.ibm.com/docs/en/zos/2.1.0?topic=functions-pthread-cond-signal-signal-condition#ptcsig)
- [pthread_mutex_lock - IBM](https://www.ibm.com/docs/en/zos/2.1.0?topic=functions-pthread-mutex-lock-wait-lock-mutex-object)
- [sem_post - Opengroup](https://pubs.opengroup.org/onlinepubs/009695399/functions/sem_post.html)
- [sem_init - Opengroup](https://pubs.opengroup.org/onlinepubs/009696699/functions/sem_init.html)
- [OpenCSF](https://w3.cs.jmu.edu/kirkpams/OpenCSF/Books/csf/html/ProcVThreads.html)