Rumah  >  Artikel  >  hujung hadapan web  >  Mari kita bincangkan tentang cara menggunakan keupayaan berbilang benang Node.js untuk melakukan pengiraan tak segerak

Mari kita bincangkan tentang cara menggunakan keupayaan berbilang benang Node.js untuk melakukan pengiraan tak segerak

青灯夜游
青灯夜游ke hadapan
2021-09-03 18:09:161915semak imbas

Bagaimana untuk melakukan pengiraan tak segerak? Artikel berikut akan memperkenalkan kepada anda cara menggunakan keupayaan berbilang benang penyemak imbas dan Node.js untuk melakukan pengiraan tak segerak, saya harap ia akan membantu anda!

Mari kita bincangkan tentang cara menggunakan keupayaan berbilang benang Node.js untuk melakukan pengiraan tak segerak

Dikatakan bahawa Node.js boleh mencapai pelayan berprestasi tinggi, tetapi apakah prestasi tinggi itu?

Semua kod perisian akhirnya dijalankan melalui CPU Sama ada CPU boleh digunakan dengan cekap adalah tanda prestasi, yang bermaksud ia tidak boleh melahu. [Pembelajaran yang disyorkan: "tutorial nodejs"]

Bilakah ia akan melahu?

  • Apabila program menjalankan rangkaian dan cakera IO, CPU melahu pada masa ini, iaitu ia melahu.
  • CPU berbilang teras boleh menjalankan berbilang program pada masa yang sama Jika hanya satu daripada teras digunakan, teras yang lain juga akan melahu.

Jadi, jika anda ingin mencapai prestasi tinggi, anda mesti menyelesaikan dua masalah ini.

Sistem pengendalian menyediakan abstraksi urutan yang berbeza yang sepadan dengan kod boleh dijalankan pada CPU yang berbeza pada masa yang sama. Ini adalah cara untuk memanfaatkan prestasi CPU berbilang teras.

Jika beberapa utas menjalankan IO, mereka akan disekat dan menunggu selesai membaca dan menulis Ini adalah cara yang agak tidak cekap, jadi sistem pengendalian melaksanakan mekanisme DMA, iaitu pengawal peranti perkakasan bertanggungjawab untuk berpindah dari peranti ke memori, dan memberitahu CPU apabila pemindahan selesai. Dengan cara ini, apabila beberapa utas melakukan IO, utas boleh dijeda dan terus berjalan selepas menerima pemberitahuan bahawa data pengangkutan DMA telah selesai.

Multi-threading dan DMA ialah penyelesaian yang disediakan oleh sistem pengendalian yang memanfaatkan CPU berbilang teras dan menyelesaikan masalah IO seperti penyekatan CPU.

Pelbagai bahasa pengaturcaraan telah merangkum mekanisme ini, begitu juga Node.js Sebab mengapa Node.js berprestasi tinggi adalah kerana reka bentuk IO tak segerak.

IO tak segerak Node.js dilaksanakan dalam libuv, berdasarkan panggilan sistem tak segerak yang disediakan oleh sistem pengendalian Ini biasanya tak segerak peringkat perkakasan, seperti DMA untuk memindahkan data. Walau bagaimanapun, beberapa panggilan sistem segerak akan menjadi tak segerak selepas dikapsulkan oleh libuv Ini kerana terdapat kumpulan benang dalam libuv untuk melaksanakan tugasan ini dan menukar API segerak kepada tak segerak. Saiz kumpulan benang ini boleh ditetapkan melalui pembolehubah persekitaran UV_THREADPOOL_SIZE dan lalainya ialah 4.

Mari kita bincangkan tentang cara menggunakan keupayaan berbilang benang Node.js untuk melakukan pengiraan tak segerak

Banyak API tak segerak yang kami panggil dalam kod kami dilaksanakan melalui urutan.

Contohnya:

const fsPromises = require('fs').promises;

const data = await fsPromises.readFile('./filename');

Walau bagaimanapun, API tak segerak ini hanya menyelesaikan masalah IO, jadi bagaimana untuk memanfaatkan CPU berbilang teras untuk pengiraan?

Node.js memperkenalkan secara eksperimen dalam 10.5 (diperkenalkan secara rasmi pada 12) modul worker_thread, yang boleh mencipta urutan dan akhirnya menjalankannya dengan berbilang CPU Ini ialah cara untuk menggunakan CPU berbilang teras untuk pengiraan.

API Asynchronous boleh menggunakan berbilang benang untuk melakukan IO dan worker_thread boleh mencipta benang untuk melakukan pengiraan untuk tujuan yang berbeza.

Untuk bercakap tentang worker_thread dengan jelas, kita perlu bermula dengan pekerja web penyemak imbas.

Pekerja web penyemak imbas

Pelayar juga menghadapi masalah tidak dapat menggunakan CPU berbilang teras untuk pengiraan, jadi html5 memperkenalkan pekerja web, yang boleh dilakukan melalui benang lain mengira.

<!DOCTYPE html>
<html>
<head></head>
<body>
    <script>
        (async function () {
            const res = await runCalcWorker(2, 3, 3, 3);
            console.log(res);
        })();

        function runCalcWorker(...nums) {
            return new Promise((resolve, reject) => {
                const calcWorker = new Worker(&#39;./webWorker.js&#39;);
                calcWorker.postMessage(nums)
                calcWorker.onmessage = function (msg) {
                    resolve(msg.data);
                };
                calcWorker.onerror = reject;
            });
        }
    </script>

</body>
</html>

Kami mencipta objek Worker, nyatakan kod js yang dijalankan pada urutan lain, kemudian hantar mesej kepadanya melalui postMessage dan terima mesej melalui onMessage. Proses ini juga tidak segerak, dan kami terus merangkumnya menjadi janji.

Kemudian terima data dalam webWorker.js, buat pengiraan, dan kemudian kembalikan hasilnya melalui postMessage.

// webWorker.js
onmessage = function(msg) {
    if (Array.isArray(msg.data)) {
        const res = msg.data.reduce((total, cur) => {
            return total += cur;
        }, 0);
        postMessage(res);
    }
}

Dengan cara ini, kami menggunakan teras CPU lain untuk menjalankan pengiraan ini Untuk menulis kod, ia tidak berbeza dengan kod tak segerak biasa. Tetapi asynchronous ini sebenarnya bukan IO asynchronous, tetapi computational asynchronous.

Urutan pekerja Node.js adalah serupa dengan pekerja web, malah saya mengesyaki bahawa nama urutan pekerja dipengaruhi oleh pekerja web.

Benang pekerja Node.js

Jika logik pengiraan tak segerak di atas dilaksanakan dalam Node.js, ia akan kelihatan seperti ini:

const runCalcWorker = require(&#39;./runCalcWorker&#39;);

(async function () {
    const res = await runCalcWorker(2, 3, 3, 3);
    console.log(res);
})();

Panggil secara tak segerak, kerana tiada perbezaan dalam penggunaan antara pengiraan tak segerak dan IO tak segerak.

// runCalcWorker.js
const  { Worker } = require(&#39;worker_threads&#39;);

module.exports = function(...nums) {
    return new Promise(function(resolve, reject) {
        const calcWorker = new Worker(&#39;./nodeWorker.js&#39;);
        calcWorker.postMessage(nums);

        calcWorker.on(&#39;message&#39;, resolve);
        calcWorker.on(&#39;error&#39;, reject);
    });
}

Kemudian pengiraan tak segerak dilaksanakan dengan mencipta objek Worker, menentukan JS untuk dijalankan dalam urutan lain, dan kemudian menghantar mesej melalui postMessage dan menerima mesej melalui mesej. Ini sangat serupa dengan pekerja web.

// nodeWorker.js
const {
    parentPort
} = require(&#39;worker_threads&#39;);

parentPort.on(&#39;message&#39;, (data) => {
    const res = data.reduce((total, cur) => {
        return total += cur;
    }, 0);
    parentPort.postMessage(res);
});

Dalam nodeWorker.js yang secara khusus melaksanakan pengiraan, dengar mesej mesej, kemudian lakukan pengiraan dan kembalikan data melalui parentPost.postMessage.

Bandingkan pekerja web, anda akan mendapati persamaan istimewa. Oleh itu, saya fikir API benang pekerja Node.js direka bentuk dengan merujuk kepada pekerja web.

Walau bagaimanapun, sebenarnya, benang pekerja juga menyokong penghantaran data melalui workerData apabila ia dibuat:

const  { Worker } = require(&#39;worker_threads&#39;);

module.exports = function(...nums) {
    return new Promise(function(resolve, reject) {
        const calcWorker = new Worker(&#39;./nodeWorker.js&#39;, {
            workerData: nums
        });
        calcWorker.on(&#39;message&#39;, resolve);
        calcWorker.on(&#39;error&#39;, reject);
    });
}

Kemudian benang pekerja boleh mendapatkannya melalui workerData:

const {
    parentPort,
    workerData
} = require(&#39;worker_threads&#39;);

const data = workerData;
const res = data.reduce((total, cur) => {
    return total += cur;
}, 0);
parentPort.postMessage(res);

因为有个传递消息的机制,所以要做序列化和反序列化,像函数这种无法被序列化的数据就无法传输了。这也是 worker thread 的特点。

Node.js 的 worker thread 和 浏览器 web woker 的对比

从使用上来看,都可以封装成普通的异步调用,和其他异步 API 用起来没啥区别。

都要经过数据的序列化反序列化,都支持 postMessage、onMessage 来收发消息。

除了 message,Node.js 的 worker thread 支持传递数据的方式更多,比如还有 workerData。

但从本质上来看,两者都是为了实现异步计算,充分利用多核 CPU 的性能,没啥区别。

总结

高性能的程序也就是要充分利用 CPU 资源,不要让它空转,也就是 IO 的时候不要让 CPU 等,多核 CPU 也要能同时利用起来做计算。操作系统提供了线程、DMA的机制来解决这种问题。Node.js 也做了相应的封装,也就是 libuv 实现的异步 IO 的 api,但是计算的异步是 Node 12 才正式引入的,也就是 worker thread,api 设计参考了浏览器的 web worker,传递消息通过 postMessage、onMessage,需要做数据的序列化,所以函数是没法传递的。

从使用上来看异步计算、异步 IO 使用方式一样,但是异步 IO 只是让 cpu 不同阻塞的等待 IO 完成,异步计算是利用了多核 CPU 同时进行并行的计算,数倍提升计算性能。

更多编程相关知识,请访问:编程视频!!

Atas ialah kandungan terperinci Mari kita bincangkan tentang cara menggunakan keupayaan berbilang benang Node.js untuk melakukan pengiraan tak segerak. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam