Fibers 和 Threads
Fibers 稱纖程,可以理解為協同程序,類似py和lua都有這樣的模型。使用Fibers可以避免對資源的互搶,減少cpu和內存的消耗,但是Fibers並不能夠真正的並行執行,同一時刻只有一個Fibers在執行,如果在其中一個Fibers中執行過多的cpu操作或者寫了個死循環,則整個主程序將卡死住。node中的異步事件循環模型就有點象這個。
Threads 又稱線程,他可以在同一時刻並行的執行,他們共享主進程的內存,在其中某一時刻某一個threads鎖死了,是不會影響主線程以及其他線程的執行。但是為了實現這個模型,我們不得不消耗更多的內存和cpu為線程切換的開銷,同時也存在可能多個線程對同一內存單元進行讀寫而造成程序崩潰的問題。
1. cluster
創始人Ryan Dahl建議,運行多個Nodejs進程,利用某些通信機制來協調各項任務。目前,已經有不少第三方的Node.js多進程支持模塊發布,而NodeJS 0.6.x 以上的版本提供了一個cluster模塊 ,允許創建“共享同一個socket”的一組進程,用來分擔負載壓力。本篇文章就基於該cluster模塊來講述Node.js在多核CPU下的編程
A single instance of Node.js runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load.
The cluster module allows you to easily create child processes that all share server ports.
官網實例
1 const cluster = require('cluster'); 2 const http = require('http'); 3 const numCPUs = require('os').cpus().length; 4 5 if (cluster.isMaster) { 6 // Fork workers. 7 for (var i = 0; i < numCPUs; i++) { 8 cluster.fork(); 9 } 10 11 cluster.on('exit', (worker, code, signal) => { 12 console.log('worker' + worker.id +'died'); 13 }); 14 } else { 15 // Workers can share any TCP connection 16 // In this case it is an HTTP server 17 http.createServer((req, res) => { 18 res.writeHead(200); 19 res.end('hello world\n'); 20 }).listen(8000); 21 }
這段代碼是有主線程 根據cups數目創建子進程,可以根據cluster.work.id打印出是哪個進程處理該請求。
2.node-threads-a-gogo
a.安裝
npm install threads_a_gogo
實例
A.- Here's a program that makes Node's event loop spin freely and as fast as possible: it simply prints a dot to the console in each turn:
//cat examples/quickIntro_loop.js
1 (function spinForever () { 2 process.stdout.write("."); 3 process.nextTick(spinForever); 4 })();
B.- Here's another program that adds to the one above a fibonacci(35) call in each turn, a CPU-bound task that takes quite a while to complete and that blocks the event loop making it spin slowly and clumsily. The point is simply to show that you can't put a job like that in the event loop because Node will stop performing properly when its event loop can't spin fast and freely due to a callback/listener/nextTick()ed function that's blocking.
1 //cat examples/quickIntro_blocking.js 2 function fibo (n) { 3 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 4 } 5 6 (function fiboLoop () { 7 process.stdout.write(fibo(35).toString()); 8 process.nextTick(fiboLoop); 9 })(); 10 11 (function spinForever () { 12 process.stdout.write("."); 13 process.nextTick(spinForever); 14 })();
C.- The program below uses threads_a_gogo
to run the fibonacci(35) calls in a background thread, so Node's event loop isn't blocked at all and can spin freely again at full speed:
1 1 //cat examples/quickIntro_oneThread.js 2 2 function fibo (n) { 3 3 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 4 4 } 5 5 6 6 function cb (err, data) { 7 7 process.stdout.write(data); 8 8 this.eval('fibo(35)', cb); 9 9 } 10 10 11 11 var thread= require('threads_a_gogo').create(); 12 12 13 13 thread.eval(fibo).eval('fibo(35)', cb); 14 14 15 15 (function spinForever () { 16 16 process.stdout.write("."); 17 17 process.nextTick(spinForever); 18 18 })();
D.- This example is almost identical to the one above, only that it creates 5 threads instead of one, each running a fibonacci(35) in parallel and in parallel too with Node's event loop that keeps spinning happily at full speed in its own thread:
//cat examples/quickIntro_fiveThreads.js
1 function fibo (n) {
2 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 3 } 4 5 function cb (err, data) { 6 process.stdout.write(" ["+ this.id+ "]"+ data); 7 this.eval('fibo(35)', cb); 8 } 9 10 var threads_a_gogo= require('threads_a_gogo'); 11 12 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 13 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 14 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 15 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 16 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 17 18 (function spinForever () { 19 process.stdout.write("."); 20 process.nextTick(spinForever); 21 })();
E.- The next one asks threads_a_gogo
to create a pool of 10 background threads, instead of creating them manually one by one:
1 //cat examples/multiThread.js 2 function fibo (n) { 3 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 4 } 5 6 var numThreads= 10; 7 var threadPool= require('threads_a_gogo').createPool(numThreads).all.eval(fibo); 8 9 threadPool.all.eval('fibo(35)', function cb (err, data) { 10 process.stdout.write(" ["+ this.id+ "]"+ data); 11 this.eval('fibo(35)', cb); 12 }); 13 14 (function spinForever () { 15 process.stdout.write("."); 16 process.nextTick(spinForever); 17 })();
F.- This is a demo of the threads_a_gogo
eventEmitter API, using one thread:
1 //cat examples/quickIntro_oneThreadEvented.js 2 var thread= require('threads_a_gogo').create(); 3 thread.load(__dirname + '/quickIntro_evented_childThreadCode.js'); 4 5 /* 6 This is the code that's .load()ed into the child/background thread: 7 8 function fibo (n) { 9 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 10 } 11 12 thread.on('giveMeTheFibo', function onGiveMeTheFibo (data) { 13 this.emit('theFiboIs', fibo(+data)); //Emits 'theFiboIs' in the parent/main thread. 14 }); 15 16 */ 17 18 //Emit 'giveMeTheFibo' in the child/background thread. 19 thread.emit('giveMeTheFibo', 35); 20 21 //Listener for the 'theFiboIs' events emitted by the child/background thread. 22 thread.on('theFiboIs', function cb (data) { 23 process.stdout.write(data); 24 this.emit('giveMeTheFibo', 35); 25 }); 26 27 (function spinForever () { 28 process.stdout.write("."); 29 process.nextTick(spinForever); 30 })();
G.- This is a demo of the threads_a_gogo
eventEmitter API, using a pool of threads:
1 //cat examples/quickIntro_multiThreadEvented.js 2 var numThreads= 10; 3 var threadPool= require('threads_a_gogo').createPool(numThreads); 4 threadPool.load(__dirname + '/quickIntro_evented_childThreadCode.js'); 5 6 /* 7 This is the code that's .load()ed into the child/background threads: 8 9 function fibo (n) { 10 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 11 } 12 13 thread.on('giveMeTheFibo', function onGiveMeTheFibo (data) { 14 this.emit('theFiboIs', fibo(+data)); //Emits 'theFiboIs' in the parent/main thread. 15 }); 16 17 */ 18 19 //Emit 'giveMeTheFibo' in all the child/background threads. 20 threadPool.all.emit('giveMeTheFibo', 35); 21 22 //Listener for the 'theFiboIs' events emitted by the child/background threads. 23 threadPool.on('theFiboIs', function cb (data) { 24 process.stdout.write(" ["+ this.id+ "]"+ data); 25 this.emit('giveMeTheFibo', 35); 26 }); 27 28 (function spinForever () { 29 process.stdout.write("."); 30 process.nextTick(spinForever); 31 })();