💪

並行&並列, 同期&非同期などの整理

2021/10/30に公開

この記事について

友達に聞かれて答えられなかったので、関連の用語を色々調べてまとめてみました。
間違ったところがあれば、ご指摘お願いいたします。
ちなみに日本語、英語、中国語の呼び方を合わせて書いています。

ジョブ、タスク、プロセス、スレッド

ジョブ / Job / ???

人間から見た仕事の単位。複数のプロセスから構成されます。

タスク / Task / ???

コンピュータから見た仕事の単位で、処理に必要なCPU、主記憶などのシステム資源を割り当てる時の単位です。
非常に誤解をまねきやすい言葉で、広義の意味では、プロセスと同じのようです。
(自分は「タスク」を使うのを避けた方がいいと思います...プロセスとスレッドだけでいいじゃない〜)
シングルタスクOS(MS-DOS等)では タスク=プロセス
マルチタスクOS(Windows等)では タスク=スレッド

マルチタスク

1つのコンピュータで、同時に複数の仕事ができること。
マルチプロセスとマルチスレッド両方できるものを指します。
ただ、Linuxにおいてマルチプロセスと呼ばれます。
マルチタスク=マルチプロセスと記載するサイトが多い気がします。

プロセス / Process / 进程

プログラムのロード、実行、終了までが1プロセス。実行中のプログラムのインスタンスとも言えます。
1つのアプリケーションから、複数のプロセスを生成できます。
1つのプロセス内で複数のスレッドを動作させることができます。
プロセスの中にスレッドがありますが、基本的にプロセス生成時に1つのスレッドしかなく、後で必要に応じでスレッドを作ります。
プロセスはCPUレジスタ&スタックが割り当てられます。

タスクとプロセスの違いは?

UNIX系のOSではタスクのことをプロセスと呼んでいます。集合で言えば、プロセスはタスクの要素ですが、広義のプロセスはタスクと同様に、「1つのプログラムを実行する処理単位」のことを指します。

マルチプロセス

マルチプロセス = プログラムを複数立ち上げることができるもの(メモ帳を複数開くとか)
forkシステムコールで、親となるプロセスをコピーして、子プロセスを生成できます。
それぞれのプロセスで異なる処理ができますが、システムリソースの消費量は、マルチスレッドより大きいです。

スレッド / Thread / 线程

プロセスから生成され、軽量プロセスとも呼ばれます。
スレッドは、子プロセスではありません。(← 間違っているサイトがありますので一言)
スレッドはCPUレジスタだけ割り当てられ、そのほかの資源は親プロセスから継承し、プロセス内の他のスレッドと共有します。
1つのスレッドは、ある時間において1つのCPUコア内で実行されます。
言い換えると、ある時間において、総スレッド数はCPUコア数より少なければ、遊んでいるコアが出てきます。

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

プロセスはスレッドを内包しています。
同じOS内の違うプロセス間は、CPUやメモリ空間等のリソースを共有しません。
→ 1つのプロセスが死んでも、他のプロセスは影響されません。
同じプロセス内のスレッド間は、CPU以外の資源を共有します。
→ 1つのスレッドが死んだら、そのスレッドを含むプロセスも死にます。

マルチスレッド

1つのプロセス内で、複数のスレッドが存在する状態のことです。
処理は複数のスレッドに分けて並行処理を行います。

並行と並列

  • 並行 / Concurrent / 并发:
    マルチプログラミングとも言われます。
    Context Switchingによって、各作業を切り替えながら、少しずつやっていく感じです。(同時にやっているように見えます)
  • 並列 / Parallel / 并行
    マルチコアCPUでなければならない。複数人が同時に作業をやる感じです。

並行と並列の違い

ご飯を食べている途中に電話がきたとします。
そのままご飯を食べ続け、食べ終わってから電話に出るなら、並行も並列も対応していないことになります。
ご飯食べるのを中断し、電話でて話が終わったら、また食事を続ける場合、並行処理と言えます。
ご飯を食べながら電話に出た場合、並列処理と言えます。

並行や並列は、どちらも複数の処理を行えますが、並行は同時じゃなく、並列は同時に行います。
コンピュータ上の並行処理は、同時にやっているように見えますが、高速に切り替えながら、各処理を少しずつやっていく感じです。

もう1つ例をあげます。
下はErlangの生みの親のJoe Armstrongさんが描いた図です。
並行と並列の違い
列 = 処理、コーヒーマシン = CPU、1列の全ての人がコーヒーを入れた = 処理を完了した
並行は、2列の人が交代で1つのコーヒーマシンを使う感じで、
並列は、2列の人がぞれぞれのコーヒーマシンを使う感じです。
1つ目の例の中で、ご飯を食べながら電話に出る=それぞれ口と耳を使うことになります。(厳密には口は並行処理をするかもしれないです笑)

スレッド数との関係

マルチスレッドは、並行にも並列にもなり得ます。
CPUまたはCPUコアが複数の場合、複数のCPUまたはコアで同時に実行されます。これが並列になります。
シングルコアCPUが1つの場合、切り替えて実行されます。これが並行処理になります。

マルチスレッド+並行処理は複雑なため、パフォーマンスが必要な領域以外は利用されることが少ないようです

小まとめ

  • 1つのシングルコアCPUの場合
    複数のプロセスは並行に実行され、それぞれのプロセス内のスレッドも並行に実行されます
  • 複数の物理CPUの場合
    複数のプロセスは、並列に複数のCPUで処理されます。
  • 1つのマルチコアCPUの場合
    複数のプロセスは、並行に1つのCPUで処理されます。その中のスレッドは並列に実行されます
  • ユーザ視点では
    並行並列関係なく、マルチプロセス&マルチスレッドだけ見えます(現代のPCは)

同期と非同期

  • 同 期 / Synchronous / 同步
    処理を順番に実行する方式。前の処理が完了しないと、次の処理が実行されないです。
    お店の前の行列と似ています。
  • 非同期 / Asynchronous / 异步
    前の処理が完了するのを待たずに、別の処理を実行します。

同期処理とスレッド数

  • シングルスレッド:
    各タスクを順番に実行していきます
  • マルチスレッド:
    プログラムはマルチスレッドに対応する場合、タスクは異なるスレッドで処理されますが、
    一度に1つのタスクしか実行されず、残りのスレッドは待ち状態になります。

非同期処理とスレッド数

  • シングルスレッド:
    並行の形で切り替えながら、同時実行しているように見えます。(並行)
  • マルチスレッド:
    異なるスレッドで同時実行します。(並列)

非同期の文法の進化

  • callback
    呼出元から呼出先に対して、完了後に実行しらい処理を引数として渡す
    → コールバック地獄になりやすく、例外を処理しにくい
    以下の例は、work01 -> work02 -> work03という流れで非同期処理をおこなっています。
work01(function(result) {
    work02(result, function(newResult) {
        work03(newResult, function(finalResult) {
            console.log("done!");
        }, failureCallback);
    }, failureCallback);
}, failureCallback);
  • promise
    データ構造の一種
    Promiseというデータ構造を返すことで、メソッドチェーンみたいな感じで使える
    .then().catch()を使う
work01().then(function(result) {
  return work02(result);
})
.then(function(newResult) {
  return work03(newResult);
})
.then(function(finalResult) {
  console.log("done!");
})
.catch(failureCallback);
  • async / await
    非同期処理を同期処理のように書くための文法
    asyncキーワードをメソッドに宣言した上で、awaitキーワードを使うことで、同期処理のような書き方ができます
async function method() {
    let result = await work01()
    let newResult = await work02()
    let finalResult = await work03()
    console.log("done!");
}

非同期処理の実現

  1. マルチスレッドによる実装
  2. イベントループによる実装

参考リスト

Discussion