プロセスとスレッドとは?Node.jsでのプロセスの開始方法とプロセス間通信

コンピュータサイエンスにおいて、プロセススレッドは二つの重要な概念です。これらはオペレーティングシステムがプログラムの実行を管理する基本単位であり、プログラムの並行性とリソース管理を理解する上で非常に重要です。本記事では、プロセスとスレッドの概念、その違いを詳しく解説し、Node.jsでプロセスを作成し、プロセス間通信を実現する方法を紹介します。

プロセスとは?

**プロセス(Process)**はオペレーティングシステムにおけるリソースの基本単位です。各プロセスは実行中のプログラムのインスタンスであり、独立したメモリ空間、データスタック、およびファイルハンドルやネットワーク接続などの他のシステムリソースを持ちます。プロセスは相互に独立しているため、あるプロセスのクラッシュや終了は他のプロセスに影響を与えません。

  • メモリ空間:各プロセスは独立したアドレス空間を持ち、他のプロセスとメモリを共有しないため、プロセス間の干渉やデータセキュリティの問題が減ります。
  • リソース管理:オペレーティングシステムは各プロセスに必要なリソース(CPU時間、メモリ、ファイルハンドルなど)を割り当てます。
  • 隔離性:プロセスは相互に独立しており、あるプロセスのエラーは通常他のプロセスに影響を与えません。

スレッドとは?

**スレッド(Thread)**はプロセス内の一つの実行パスです。一つのプロセスには複数のスレッドが含まれることがあり、これらのスレッドはプロセスのリソース(メモリ空間やファイルハンドルなど)を共有しますが、各スレッドには独自のスタック空間とプログラムカウンタがあります。

  • リソースの共有:同一プロセス内のスレッドはリソースを共有するため、スレッド間の通信は速く、コストも低いです。
  • 軽量:スレッドの作成、終了、コンテキストスイッチのコストはプロセスよりも低いです。
  • 並行実行:複数のスレッドがマルチコアCPUで並行して実行されるため、プログラムの実行効率が向上します。

プロセスとスレッドの違い

特性プロセススレッド
リソースの独立性プロセス間は相互に独立し、それぞれにリソースとメモリ空間を持つ。スレッドは同一プロセスのリソースとメモリ空間を共有する。
オーバーヘッドプロセスの作成と終了には大きなオーバーヘッドがあり、独立したリソースが必要。スレッドのオーバーヘッドは小さく、作成、終了、切り替えが速い。
通信プロセス間通信は複雑で、通常はオペレーティングシステムの提供するメカニズムが必要。スレッド間通信は比較的容易で、メモリ空間を共有するため。
安定性一つのプロセスのクラッシュは他のプロセスには影響しない。一つのスレッドのクラッシュはプロセス全体をクラッシュさせる可能性がある。
並行性プロセス間の並行実行は独立しており、マルチタスク処理に適している。スレッドは並行実行をサポートし、単一プロセスの実行効率を向上させる。

Node.jsでプロセスを開始する方法

Node.jsでは、プロセスの作成と管理にいくつかの方法が提供されており、child_process モジュールが最も一般的に使用されます。このモジュールを使って、子プロセスを作成し、プロセス間通信を行うことができます。以下は、いくつかの一般的な方法です。

1. exec

exec はシェルコマンドを実行し、そのコマンドの出力をコールバック関数で返します。短時間で終了するプロセスに適しています。

1
const { exec } = require("child_process");
2
3
exec("ls -l", (error, stdout, stderr) => {
4
if (error) {
5
console.error(`Error: ${error.message}`);
6
return;
7
}
8
if (stderr) {
9
console.error(`Stderr: ${stderr}`);
10
return;
11
}
12
console.log(`Stdout: ${stdout}`);
13
});

2. spawn

spawnexec に似ていますが、子プロセスオブジェクトを返し、stdoutstderr ストリームを通じて出力を取得できます。長時間実行されるプロセスに適しています。

1
const { spawn } = require("child_process");
2
3
const ls = spawn("ls", ["-l"]);
4
5
ls.stdout.on("data", (data) => {
6
console.log(`Stdout: ${data}`);
7
});
8
9
ls.stderr.on("data", (data) => {
10
console.error(`Stderr: ${data}`);
11
});
12
13
ls.on("close", (code) => {
14
console.log(`Child process exited with code ${code}`);
15
});

3. fork

fork は Node.js の子プロセスを作成するために使用され、プロセス間通信に適しています。forkspawn の特殊なケースで、Node.js 子プロセスを作成するために使用され、通信チャネルが組み込まれています。

1
const { fork } = require("child_process");
2
3
const child = fork("child.js");
4
5
child.on("message", (message) => {
6
console.log(`Received message from child: ${message}`);
7
});
8
9
child.send("Hello from parent");

プロセス間通信(IPC)

Node.js では、プロセス間の通信は message イベントを通じて実現できます。特に fork で作成された子プロセスでこれを使用します。親プロセスと子プロセスはお互いにメッセージを送受信できます。

  • 親プロセスから子プロセスへメッセージを送信

    1
    const { fork } = require("child_process");
    2
    const child = fork("child.js");
    3
    4
    child.send({ hello: "world" });
  • 子プロセスがメッセージを受信し、返信する

    1
    process.on("message", (message) => {
    2
    console.log(`Received message from parent: ${message.hello}`);
    3
    process.send({ reply: "Hello from child" });
    4
    });

このメッセージパッシング方式により、プロセス間通信がシンプルかつ効率的になり、プロセス間でメモリを共有せず、各プロセスの独立性と安定性が保証されます。

プロセスとスレッドの概念を理解し、Node.jsでそれらを使用する方法を知ることで、開発者はより効率的で安定したアプリケーションを作成することができます。プロセスとスレッドは並行プログラミングにおいて重要な役割を果たしており、Node.jsが提供するツールはこれらの操作をより便利にします。