2020 年 11 月 === # 蒼時 ## Parallelism(並行) 和 Concurrency(並發) 要討論 Thread 的第一件事情就是先把「並行」跟「並發」區分出來。 Parallelism 是指「同時」的處理,也就是在多個 CPU 上同時處理任務。而 Concurrency 則是「交替」的處理,雖然從結果上來看可能都是把事情做完,但並發實際上並非真正的同時處理,只是因為處理速度足夠快而讓人感覺是「同時」進行的。 可以參考[這篇文章](https://medium.com/mr-efacani-teatime/concurrency%E8%88%87parallelism%E7%9A%84%E4%B8%8D%E5%90%8C%E4%B9%8B%E8%99%95-1b212a020e30)的插圖 ## Race Condition(競爭條件) 當程式同時處理的時候,就可能會因為同時對某一個數值「寫入」造成預期外的結果。 舉例來說,假設我們進行一個 `a++` 的處理,現在同時有兩個 Thread 在執行 初始狀態:`a = 1` Thread 1:`a++` => `a = 1` => `a = a + 1` => `a = 2` Thread 2:`a++` => `a = 1` => `a = a + 1` => `a = 2` 此時我們原本預期 `a = 3` 但實際上因為 Thread 1 和 Thread 2 都讀取到的是 `a = 1` 因此最後結果都會變成 `a = 2` 而不是我們預期的 `a = 3` > 其實這跟在處理資料庫的 Phantom Read(幻讀)情境是類似的,另外即使是 Concurrency 也是會發生 Race Condition 的 ## Mutex(互斥鎖) 透過 Mutex 就可以避免 Race Condition 的發生,這是因為當我們使用 Mutex 之後就會限制能夠存取的 Thread 同時間只能有一個,進而堵塞其他 Thread 運行。 以 Ruby 為例子,會像這樣 ```ruby lock = Mutex.new # Thread 1 Thread.new do lock.synchronize do sleep 1 puts 'Thread#1' end end # Thread 2 Thread.new do lock.synchronize do sleep 1 puts 'Thread#2' end end # ... ``` 正常狀況下我們會看到 `Thread#1` 和 `Thread#2` 同時在畫面上被顯示出來,但是如果我們加上了 Mutex 之後,就需要等待 `Thread#1` 或者 `Thread#2` 先印出來後,再等待一秒才會印出另一個人。 這就表示 Mutex 限制了通時間只能有一個人進行處理,進而避免在同一個瞬間將變數寫入的情況。 ## GIL/GVL(Global VM Lock) Ruby 為了確保多執行緒(Multi-Thread)的穩定性因此以 Thread 為基礎加入了 Mutex 來處理,這就是 GVL 的設計,也因為這一層設計造成我們在 Ruby 運行時無法用最高效的方式運行。 > CRuby 使用的 Thread Library 叫做 `pthread` 是能夠在多核心上運行 Thread 的套件,因此 Ruby 的 Thread 是能在多核心上運作的,只是因為 GVL 的關係無法確實善用。 ```ruby a = [] threads = Array.new(100_00).map.with_index do |_, i| Thread.new do a << i end end sleep 0 until threads.map(&:stop?).reduce(:&) puts a.size ``` 像是上面這樣共用一個 `a` 變數的時候,就會自動觸發 GVL 來確保每一個 Thread 不會同時的被執行。 > 如何驗證的方式目前還沒有結論還需要再做研究,關於 GVL 對運行速度的影響可以參考[這篇文章](https://www.speedshop.co/2020/05/11/the-ruby-gvl-and-scaling.html)的說明 ## Ractor 在 Ruby 3.0 預定增加的功能,不過 Ruby 2.7 就能夠使用到,實際上跟 Thread 類似但是做了一個不一樣的改變就是 GVL 的隔離。 簡單來說一個 Ractor 會有一個自己的 GVL 如此一來在操作的時候就不會因為互相搶佔 GVL 而出現效能上的問題,以往操作都會需要透過 Main Thread 上的 GVL 等待解鎖才能夠進行下一個操作,但是改由 Ractor 之後則會因為有各自的 GVL 而不會互相干擾,但相反的這樣的機制會讓我們無法在不同的 Ractor 之間共用變數。 # Cindy Thread 為 CPU utilization 的最小單位,有自己的 program counter、stack、registers Multi-threading vs Multi-processing --- - [Thread](https://www.kshuang.xyz/doku.php/operating_system:course_concept:thread) - [【恐龍】理解 Process & Thread](https://medium.com/erens-tech-book/%E7%90%86%E8%A7%A3-process-thread-94a40721b492) - [[CS] 進程與線程的概念整理(process and thread)](https://pjchender.github.io/2020/09/20/cs-%E9%80%B2%E7%A8%8B%E8%88%87%E7%B7%9A%E7%A8%8B%E7%9A%84%E6%A6%82%E5%BF%B5%E6%95%B4%E7%90%86%EF%BC%88process-and-thread%EF%BC%89/) - [Multiprocessing in Ruby - a Good Alternative to Threads?](https://naturaily.com/blog/multiprocessing-in-ruby) - [多執行緒 (Multi-thread)](https://sls.weco.net/node/21324) - [Threading and Code Execution in Rails](https://guides.rubyonrails.org/threading_and_code_execution.html#automatic-concurrency) - [Tuning the application process and thread count ](https://www.phusionpassenger.com/library/config/nginx/optimization/#tuning-the-application-process-and-thread-count) - [Web Server / Application Server / Rack / Process / Thread](https://mgleon08.github.io/blog/2016/10/23/web-server-application-server-rack-process-thread/) - [分身之術(?) Thread](https://5xruby.tw/posts/what-is-thread/) - [Program/Process/Thread 差異](https://medium.com/@totoroLiu/program-process-thread-%E5%B7%AE%E7%95%B0-4a360c7345e5) - [Ruby on Rails 伺服器的選擇](https://blog.chh.tw/posts/ruby-on-rails-server-options/) - [Deploy Rails App](https://sites.google.com/site/zhoubx/computer/ruby-on-rails/deploy-rails) - [HTTP Server 大小事](https://old.michaelhsu.tw/2013/07/04/server/) - [高效能 Web 伺服器開發](https://hackmd.io/@sysprog/fast-web-server?fbclid=IwAR2l0-EXiL3CWWgmfrybqM-6rWmGmp4zOKotbxEpsKT7JNu5MZnvrN7hcac)