💡

スレッドとプロセスの違いを完全に理解する

2024/05/01に公開

はじめに

こんにちは、FarStep です。
プログラミングを学ぶ中で、「プロセス」と「スレッド」という言葉を耳にしたことがある方は多いと思います。
しかし、これらの違いを明確に説明できる自信がない方も多いのではないでしょうか。

本記事では、プロセスとスレッドの違いについて、エッセンスを抽出して 解説します。
説明を簡潔にしましたので、本記事は 5 分程度で読み終えることができます。

本記事の内容を自分の言葉で説明できるようになれば、プロセスとスレッドの違いの理解は十分でしょう。
それでは、始めます 🚀

プログラムとは

プロセスとスレッドの違いを理解する前に、まずは「プログラム」について理解しましょう。

プログラムとは、プログラミング言語で書かれた一連の命令 のことです。
プログラミング言語の例としては、以下のようなものがあります。

  • C 言語
  • Java
  • Python
  • Ruby
  • JavaScript

プログラムは、プログラミング言語で書かれたソースコードを、コンピュータが理解できる バイナリコード に変換することで実行可能になります。

バイナリコードへの変換は、以下の 2 つの方法で行われます。

  • インタプリタ による逐次変換
  • コンパイラ による事前変換

変換されたバイナリコードは メモリ上に展開 され、CPU(中央演算処理装置)によって 逐次実行 されます。

プログラムの例としては、以下のようなものがあります。

  • Microsoft PowerPoint などのアプリケーションのコード
  • ウェブサイトのバックエンドのコード

このように、私たちが日常的に使用しているソフトウェアは、全てプログラムとして実装されています。
プログラムについて理解できたところで、次は「プロセス」について見ていきましょう。

プロセスとは

プロセスとは、実行中のプログラムのインスタンス のことです。簡単にいうと、「プログラムの実行単位」ですね。プログラムが起動されると、オペレーティングシステム(OS)によってプロセスが生成されます。

プロセスは、プログラムの実行に必要な様々な リソース を管理します。
プロセスが管理するリソースには、以下のようなものがあります。

  • コードセグメント(実行可能なコード)
  • データセグメント(グローバル変数や静的変数)
  • ヒープ(動的メモリ割り当て)
  • スタック(ローカル変数、関数パラメータ、関数呼び出し)
  • レジスタ(CPU の高速な一時的データ格納場所)

各プロセスは、独立したメモリアドレス空間 を持っています。
これにより、あるプロセスが他のプロセスのメモリを破損してしまうことを防ぐことができます。

ただし、プロセス間でリソースにアクセスするためには、プロセス間の切り替え が必要になります。
このプロセス間の切り替えには、一定の実行時間がかかってしまいます。
よく オーバーヘッドが大きい と言われますね。

プロセスの具体例として、ブラウザの Google Chrome を挙げます。

  • Google Chrome では、各タブが独立したプロセスとして実行される(プログラムのインスタンス)。
  • Chrome は、アイドル状態のタブのメモリ割り当てを減らすなどの最適化を行っている(リソースの管理)。

このように Google Chrome の各タブはプロセスのようですね。

プロセスについて理解できたところで、次は「スレッド」について見ていきましょう。

スレッドとは

スレッドとは、プロセス内の実行単位 のことです。
スレッドには二種類あります。

  • シングルスレッドプロセス
    一つのスレッドを持つプロセスのことを指します。

  • マルチスレッドプロセス
    複数のスレッドを持つプロセスのことを指します。

マルチスレッドプロセスでは、複数のスレッドが同時に動作することができます。
各スレッドは、独自のレジスタとスタックを持っています。
ただし、コードセグメント、データセグメント、ヒープなどの他のリソースは、スレッド間で共有されます
スレッドは、同じプロセス内の他のスレッドと同じメモリアドレス空間を共有しているため、スレッド間の通信は非常に効率的です。
しかし、これは同時に、一つのスレッドがプロセス全体を破損させる可能性があることを意味します。

下記の画像はシングルスレッドとマルチスレッドを図解したものです。
黒い糸のようなものがスレッドです(スレッドは英語で「糸」という意味です)。


シングルスレッドプロセス


マルチスレッドプロセス(独自のスタックとレジスタを持つがそれ以外は共有)

スレッドの具体例として、Web サーバーである Apache を挙げます。

  • Apache は、スレッドを使用して複数のクライアントリクエストを効率的に処理する。
  • スレッドを使用することで、各リクエストに対して別々のプロセスを作成するオーバーヘッドなしで、サーバーは同時接続を処理できる。

このように、多くの Web サーバーはスレッドを使用して効率的にリクエストを処理しています。

プロセスについて理解できたところで、最後に「プロセスとスレッドの違い」について見ていきましょう。

プロセスとスレッドの違いについて

プロセス、スレッドについて理解できたでしょうか。
プロセスとスレッドの主な違いをまとめると、次のようになります。

  • リソースの管理単位

    • プロセスは、コードセグメント、データセグメント、ヒープ、スタック、レジスタなどのリソースを独立して管理する
    • スレッドは、レジスタとスタックのみを独自に管理し、他のリソースはプロセス内の他のスレッドと共有する
  • メモリアドレス空間

    • 各プロセスは、独立したメモリアドレス空間を持つ
    • 同じプロセス内のスレッドは、同じメモリアドレス空間を共有する
  • 並行処理

    • マルチスレッドプロセスは、複数のスレッドを同時に実行することで並行処理を実現する
    • プロセス間の並行処理は、プロセス間通信を介して実現する必要がある
  • オーバーヘッド

    • プロセス間の切り替えには、一定の実行時間がかかる
    • スレッド間の切り替えは、プロセス間の切り替えよりも高速である
  • 安全性

    • プロセス間では、他のプロセスのメモリを破損することはできない
    • スレッド間では、一つのスレッドがプロセス全体を破損させる可能性がある

以上のように、プロセスとスレッドには、リソースの管理単位、メモリアドレス空間、並行処理、オーバーヘッド、安全性などの点で違いがあります。

コラム:Go 言語のゴルーチンについて

プロセスとスレッドの違いについて理解したところで、Go 言語のゴルーチンについて考えてみましょう。
ゴルーチンは「プロセス」なのでしょうか?それとも「スレッド」なのでしょうか?

結論から言うと、ゴルーチンは スレッドに近い概念 です。
ただし、ゴルーチンは、オペレーティングシステムのスレッドとは異なる、Go 言語独自の実装になっています。

ゴルーチンは、以下のような特徴を持っています。

  • 複数のゴルーチンが同じメモリ空間を共有する
  • ゴルーチン間の切り替えはオペレーティングシステムのスケジューラではなく、Go 言語のランタイムが制御する
  • ゴルーチンは、オペレーティングシステムのスレッドよりも軽量である

これらの特徴は、スレッドの特徴に近いです。
ゴルーチンは、スレッドよりも軽量であるため、大量のゴルーチンを生成しても、オーバーヘッドが小さくなっています。

また、ゴルーチン間の切り替えが、Go 言語のランタイムによって制御されるため、オペレーティングシステムのスケジューラによる切り替えよりも効率的です。

このように、ゴルーチンは、スレッドに近い概念ではありますが、Go 言語独自の実装になっています。

まとめ

本記事では、プロセスとスレッドの違いについて解説しました。
プロセスとスレッドの違いを理解することは、効率的で安全なプログラムを書くために重要です。
プロセスとスレッドの違いについて、理解が深まったのであれば幸いです。

参考文献

Discussion