マルチプロセス(マルチスレッド)のプログラムってとても難しいですね。
特にJavaScript(TypeScript)の場合はことさら難しいと思いました。
そこで、練習のためにマルチプロセスでのFizzBuzzを以下の仕様を満たすように作ってみました。
- FizzBuzz値を算出する複数のワーカープロセス(ワーカースレッド)を使う
- ワーカープロセス(ワーカースレッド)からメインプロセス(メインスレッド)に算出した結果を送信する
- メインプロセス(メインスレッド)では(逐次出力ではなく)結果がすべて揃った後に、結果をまとめて出力する
- 出力する形式(文字列)の細かいところは問わないこととする
以下は、Node.jsのclusterを使ったマルチプロセス版(TypeScript)です。
RxJSだったらどうなるのか少し気になりますね。
もっと簡単にできるのでしょうか?
fizzbuzz.ts
import * as cluster from "cluster";
import * as events from "events";
events.EventEmitter.defaultMaxListeners = 30;
function* range(begin: number, end: number) {
for (let i = begin; i <= end; i++) {
yield i;
}
}
function fizzbuzz(i: number) {
return i % 15 === 0 ? "FizzBuzz"
: i % 5 === 0 ? "Buzz"
: i % 3 === 0 ? "Fizz"
: i.toString();
}
async function main() {
if (cluster.isMaster) {
const promises = Array.from(range(1, 30)).map((i) => {
return new Promise<[number, string]>((resolve) => {
cluster.on("message", (worker, [index, result]: [number, string]) => {
if (i === index) {
resolve([i, result]);
}
});
});
});
for (const i of range(1, 30)) {
const worker = cluster.fork();
worker.on("online", () => {
worker.send(i);
});
}
const map = new Map(await Promise.all(promises));
console.log(map);
Object.values(cluster.workers).forEach((w) => w!.kill());
} else {
process.on("message", (i) => {
process.send!([i, fizzbuzz(i)]);
});
}
}
if (require.main === module) {
main();
}
ポイントはPromiseを作ってからfork/sendして、そのあとawaitで待つことです。
結果は以下のようになります。
> Executing task: npm-run ts-node src/fizzbuzz.ts <
Map {
1 => '1',
2 => '2',
3 => 'Fizz',
4 => '4',
5 => 'Buzz',
6 => 'Fizz',
7 => '7',
8 => '8',
9 => 'Fizz',
10 => 'Buzz',
11 => '11',
12 => 'Fizz',
13 => '13',
14 => '14',
15 => 'FizzBuzz',
16 => '16',
17 => '17',
18 => 'Fizz',
19 => '19',
20 => 'Buzz',
21 => 'Fizz',
22 => '22',
23 => '23',
24 => 'Fizz',
25 => 'Buzz',
26 => '26',
27 => 'Fizz',
28 => '28',
29 => '29',
30 => 'FizzBuzz' }