go/node/python 多进程与多核cpu
node
node單線程,沒有并發,但是可以利用cluster進行多cpu的利用。cluster是基于child_process的封裝,幫你做了創建子進程,負載均衡,IPC的封裝。
const cluster = require('cluster'); const http = require('http');if (cluster.isMaster) {let numReqs = 0;setInterval(() => {console.log(`numReqs = ${numReqs}`);}, 1000);function messageHandler(msg) {if (msg.cmd && msg.cmd === 'notifyRequest') {numReqs += 1;}}const numCPUs = require('os').cpus().length;for (let i = 0; i < numCPUs; i++) {cluster.fork();}for (const id in cluster.workers) {cluster.workers[id].on('message', messageHandler);}} else {// Worker processes have a http server.http.Server((req, res) => {res.writeHead(200);res.end('hello world\n');process.send({ cmd: 'notifyRequest' });}).listen(8000); }我們通過cluster.fork()來創造幾個子進程,讓子進程來替我們工作。在fork的時候會傳一個參數到子進程,cluster.isMaster就是根據有沒有這個參數判斷的。
如果是子進程就起一個server。
每個子進程都會綁定到8000端口,這不會引起端口占用嗎?
答案是不會,因為listen并不會真的監聽到8000端口,它會通過IPC把子進程的消息傳到主進程,主進程會創建服務器,然后調用子進程的回調。
在子進程的回調中:子進程會根據主進程是否返回handle句柄來執行下一步的操作,如果沒有handle句柄,說明在負載均衡的策略沒有選中本進程。那么就自己造一個handle對象返回。
那自己造個對象怎么返回請求呢?
請求到主進程,主進程會分發請求,處理到該請求的子進程會通過IPC與主進程通信,這樣就完成了一個請求的響應。
通過cluster完成單機器的負載均衡,那么多機器呢?還是得用nginx。
node & pm2
pm2 是node的進程管理工具,它封裝了cluster,可以通過命令行來創建多個進程來處理。
寫個config文件:
app.json
也可以不寫配置文件直接寫pm2 start -i 4 --name server index.js
開啟4個instance。
通過參數開啟多個子進程,而不需要修改我們的業務代碼。
go
go也是非阻塞io,Golang默認所有的任務都在一個cpu核里,如果想使用多核來跑goroutine的任務,需要配置runtime.GOMAXPROCS。
自從Go 1.5開始, Go的GOMAXPROCS默認值已經設置為 CPU的核數,我們不用手動設置這個參數。
我們先說說go的并發。
go本身就可以通過go關鍵字來進行并發操作。go關鍵字創建的并發單元在go中叫goroutine。
比如:
會打印789 ,123,456,或者 780,456,123。
在主線程開始就通過go字段開啟了2個goroutine,兩個goroutine的執行順序不確定。
如果當前goroutine發生阻塞,它就會讓出CPU給其他goroutine。
如果當前goroutine不發生阻塞,一直在執行,那么什么時候執行其他goroutine就看go調度器的處理了。
不過go提供runtime.Gosched()來達到讓出CPU資源效果的函數,當然不是不執行,會在之后的某個時間段執行。如果把注釋去掉,789就會最后執行。
單核的時候其實goroutine并不是真的“并行”,goroutine都在一個線程里,它們之間通過不停的讓出時間片輪流運行,達到類似并行的效果。
如果我在123,或者456之前加 time.Sleep(time.Second)。那么CPU的資源又會轉讓回主進程。
當一個goroutine發生阻塞,Go會自動地把與該goroutine處于同一系統線程的其他goroutines轉移到另一個系統線程上去,以使這些goroutines不阻塞,主線程返回的時候goroutines又進入runqueue
下面這段代碼:
import ("fmt""runtime" )var quit chan int = make(chan int)func loop() {for i := 0; i < 100; i++ { //為了觀察,跑多些fmt.Printf("%d ", i)}quit <- 0 }func main() {runtime.GOMAXPROCS(1)go loop()go loop()for i := 0; i < 2; i++ {<-quit} }會打印什么呢?
runtime.GOMAXPROCS(2)改成雙核cpu,又會打印什么呢?
我們能看到,雙核cpu的時候,goroutine會真正的并發執行而不是并行。他們會搶占式的執行。
參考https://studygolang.com/articles/1661
python
python是有多線程的,但是python有gil影響了他的多cpu利用。
GIL是CPython中特有的全局解釋器鎖
這把鎖在解釋器進程中是全局有效的,它主要鎖定Python線程的CPU執行資源。
想要執行多核的進程需要滿足2個條件
python在單核cpu上執行沒有問題,這個線程總能獲得gil,但是在多核的時候,線程會出現競爭,GIL只能同時被一個線程申請到,沒申請到的就會被阻塞,就會一直處于閑置狀態。
到線程切換時間然后睡眠,被喚醒之后獲取gil又失敗,惡性循環。
特別是計算型線程,會一直持有gil。
GIL 可以被 C 擴展釋放,Python 標準庫會在每次 I/O 阻塞結束后釋放 GIL,因此 GIL 不會對 I/O 服務器產生很大的性能影響。因此你可以 fork 進程或者創建多線程來創建網絡服務器處理異步 I/O,GIL 在這種情況下并沒有影響。
解決方案:
Python 3.2開始使用新的GIL。新的GIL實現中用一個固定的超時時間來指示當前的線程放棄全局鎖。在當前線程保持這個鎖,且其他線程請求這個鎖時,當前線程就會在5毫秒后被強制釋放該鎖。
總結
node是沒有多線程的利用的,只能用多進程來利用多核cpu,python因為gil的問題,也沒法完全利用多線程,但是有一些神奇的方案可以利用比如指定cpu運行。
go的實現是比較好的,畢竟是后來的語言,可以多核跑協程,來利用cpu
轉載于:https://www.cnblogs.com/dh-dh/p/9174294.html
總結
以上是生活随笔為你收集整理的go/node/python 多进程与多核cpu的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Distinct Subsequence
- 下一篇: ES6中表达export default