RareTECH受講生ブログ

RareTECHの受講生がアウトプットに利用しているブログです。

初心者がWebシステムにおける同期や非同期処理についてざっくりと解説

同期と非同期

最近、RareTECHの名前の由来が気になり始めた年頃の受講生です。

今回お話しするのは、8月22日に受けた講義の「同期処理」と「非同期処理」についてです。

あくまでざっくりと話します。

同期処理とは

複数のタスクがある場合、一つずつ上から順番に処理することを言います。

この処理のことを別名、フロー処理、随時処理と言います。

----------------> ループ実行 --------------------> hello world 実行------------->

このように一直線状になっているものをスレッドと言います。

この後も出てくるので覚えておきましょう。

処理が左から右へと実行されているのがわかりますね。

実際にどうなっている処理なのか、詳しく説明してきます。

まず、アプリケーションコードが、「ループ一億回(!?)」の処理と「hello world」を処理するという命令を作り、「スレッドを作ってねー」と言います。

メモリが「OKよー」と言って、プロセスの中に、スレッドを生やします。

プロセス!?

スレッド!?

となるかもしれませんがそういうものがある程度に今は認識として留めておいてください。後述します。

CPUとメモリの間にはOSが介在します。

メモリの中のプロセスの中のスレッドがOSを通してCPUの中のコアに

「 此度(こたび)の件、平にご容赦お願い申し上げる」

と実行処理をお願いするわけですね。

そこで、コアが

「ははあー、スレッド殿、承知仕ったでござる」

とかいって聞き入れます。

そこで実行処理するのが流れです。

ただこの流れですとね、

一億回のループしてから、hello world が出てくるのですごーーーーーーく時間がかかりますよね。

もうやんなっちゃう。

これを解決したのが次にお話しする非同期処理です。

技術というのは、不便から生まれもっと快適にしたいと考えることから生まれる

非同期処理とは

同期処理では、上から順番に処理する関係で、長い処理もいちいち待たないといけないのがネックでした。

それを解決する考えとして出てきたのが、CPUの中に複数のコアがあるなら、それぞれ別々の処理を走らせればいいということです。

後述する、マルチスレッドというものをここでちょこっと出します。

簡単に言うと、 プロセスの中にスレッド増やしまくればいいというお話です。

つまりこういうことです。

コア1------------------------------------> hello world 実行------------->

コア2------------------------------------> ループ実行------------->

そうですね、

ループ一億回(コア1)とhello world(コア2)に分けることによって

ループを待たずしてこんにちは世界がいえるということができるようになりました。

ここで

あれ、これもしかして、同時に複数の処理が実行できる!?

もしかしてこれが並列処理じゃね?!

と勘の良い人なら思ってくれそうですよね。

並列に走らせるから並列処理というわけです。

他に並行処理というものがあるのですが何かというと

複数の実行状態を実現していること!

並列は並列に処理を走らせているのですが、並行処理というものは、別に並行させてなくても

とりあえず、複数の実行状態を実現させていればOKです。

なので、

並行処理のなかに並列処理が含まれ

一つのコアが処理を高速で切り替えることも並行処理の一部となっています

なぜ並行・並列処理が必要になったの?

これに関しては、

実は逆に今まで、CPUコアが複数あるのはお金持ちじゃないと無理だったので実現性がなかったことが挙げられます。

さらにCPUの性能向上によるものも大きいです。

どういうことかというと、サーバー内部の処理がめっちゃ速くなったのですね。

クライアントからサーバーへリクエストを投げるまでの処理がめっちゃ早い!

それに対し、ウェブサーバーからDBに接続して、データのレスポンスを待つ間が遅く感じるようになったのですね。

そもそもコアが複数あれば、DBを待つ間、新しい処理ができるじゃんというわけですね。

プロセス・スレッドとは何か。

それでは、ここまで説明を割愛したプロセス、スレッドについて深掘りしていきましょう。

スレッドとメモリの違いを書きます。

スレッドとは

CPUに命令できるよ

一つのプロセス内はアプリケーションコードによって複数のスレッドを作成できます。

同一メモリ内のスレッド内のデータ共有が簡単だよということです。

仮想メモリ共有ができる。

しかし、複数スレッドがあると後述するメモリ空間というものが共有されている関係で排他制御なるものを実行しないといけなくてちょっと面倒

一つのスレッドで使えるメモリの上限は2メガバイト

スレッドが増えるとメモリ消費量が増えます

プロセスとは

実はアプリケーションコードによってプロセスも複数作成できます。

1プロセスに対し仮想メモリ空間なるものが与えらます。

プロセスを分けることによって、仮想メモリ空間が分けられるのでちょっと便利

しかし、スレッドと違い、プロセス同士のデータ共有が難しいです。

というものが挙げられます。

メモリ空間とは何ぞや

さて、ここに出てきた、メモリ空間とはなんぞやとなりますよね。

・・・・

仮想メモリ空間とは、物理メモリを使っているが、仮想的な俺だけのフィールドを展開すること

プロセス俺さん

俺だけが使えるよ?

俺だけが使えるフィールだぜwwwwwwwwwwww

俺だけのフィールドだぜwwwwwwwwwwwwwwwwwwwww

ということらしいです。

きっと授業で何度も講師の方が俺だけのフィールドだぜと強調されていたのできっと重要なんでしょう。

そういうことらしいので覚えておきましょう。

どうやって仮想メモリ空間を定義するの

さて、どうやって仮想メモリ空間を定義するのか

実は1プロセスを作成すると、プロセスさんが、物理メモリに「俺のフィールドも入れてくれよ」と要請するので

ページテーブルなるものが出来上がりました。

これは、仮想アドレスと物理アドレスを変換するためのテーブルです。

こういうものです。

いやいや、物理メモリに直接保存するメモリを指定すればいいじゃないかと思いますよね。

このメリットは、複数プロセスを作った時に現れます。

プロセスの構造自体、一つのプロセスをコピーして作る(フォーク)と同じ感じなのが出来上がるんですが

同様のメモリをプロセスから参照しているから、仮想アドレスも同じじゃんとなるわけです。

仮想アドレスだけ同じならページテーブルで変換することにより参照先のアドレスは一緒なんだからいいじゃないかとなるわけです。

プロセスから見えるのは同じメモリ、だけど事実上内部で示しているメモリは違うよということをページテーブルを使うことによって実行できるということなんですね〜

メモリ空間の中身

プログラムや静的データ | 共有ライブラリ | スタックなど | カーネルなど

PDAプロセッサーデータエリア(CPUのコアがメモリから読み取ってどこまで実行していルカの状態を格納するやつ レジスタという)

PDAは並列処理が増えてPDAの処理の参照が増えるとコンテキストスイッチコストとなります。

コンテキストスイッチコストとは、複数のコアの切り替えコストがかかり結果的に遅くなることを指します。

マルチスレッドVSマルチプロセス

マルチプロセス

  • メモリ空間が分かれていることによりスレッドセーフ(非同期実行時にメモリ干渉が起きない)

  • メモリが別なので状態が分けられる、webapp内のメモリの動作の管理が楽

  • プロセス同士のデータの共有がしづらい

  • コンテキストスイッチコストが大きい(CPUがプロセス、スレッドを切り替えるときのコスト)

特に大まかな計算処理し習いならマルチプロセスの方がいい

マルチスレッド

  • メモリ空間が共有されているので、スレッド同士で値がやりとりできる
  • メモリ消費量が少ない(とはいっても2メガバイトまで)
  • スレッドの作成が早い
  • しかし、仮想メモリ空間を共有していることによる排他制御しないといけない

排他制御とは

例えば、変数に対して何かしらの処理をするとき、複数のスレッドが同時実行するときに出る不整合の発生

同時に実行することで片方のみしか処理されないなどの問題が出てきてしまうため、一つのスレッドのみしか読み書きできないように制御することによって、不整合を発生させないようにする処理のことです。

マルチプロセス・マルチスレッドに関する覚えておきたい用語

フォーク

元のプロセスをコピーし新しく作ることです

Pool

フォークやスレッドを生やすのに時間がかかるため、先に、プロセスやスレッドを用意しておくことを指します

コアの数以上、プロセスやスレッドを作っても意味がないため、コアの数だけ作っておくことです

事前にフォークすることをプリフォークと呼んだりします。

ウェブサーバーアーキテクチャとC10K問題

さてここからは実際のウェブサーバーの処理の流れについて見てみましょう

クライアントがウェブサーバーに対してHTTPリクエストをして、ウェブサーバーがHTTPリクエストの受理して

HTTPリクエストの解析をした上で、リクエストに応じた処理(問題があった場合エラーとして処理)をしてからクライアントにレスポンスをしてコネクションを閉じます。

ウェブサーバーは、複数のクライアントからリクエストが想定されることから、並列処理が必要になってくるのです。

複数リクエストの処理には、Prefork型とイベント駆動型に分けられます。

Prefork型は、マルチプロセス、マルチスレッドのことを指します。

しかしこれには問題があります。

C10k問題です。

Cはクライアント、10kとは10kbのことを指します(1kbは1000b)

一万台のクライアントが一気にサーバーにリクエストを投げたら処理性能がサーバー側が効率よく処理できなくて困っちゃう問題のことです。

ウェブサーバーでいえば、Apacheとかがそうですね。

一方Nginxという、「ハイブリッド型(イベント駆動型+マルチプロセス)」を使っていると、この問題をうまく解決しました。

C10k問題が起こった理由は、クライアントが多すぎて処理しきれないわ、でしたね。

どうすればいいのか、同期処理であるシングルスレッドを使うしかない。

うーむ、それだと処理が長くなる。

それを解決したのがイベント駆動型です。

イベント駆動型は一つのプロセスで複数のリクエストを処理するために非同期I/Oというものを使用します。

一つのスレッドだと待ち状態が発生するのですがこの非同期I/Oなるものを使えば、別の処理を実行してくれます。

データ受信をイベントとして受け取り処理を切り替えるのです。

あれです、Javascriptで使われる、コールバック関数があるかと思いますが、それを非同期処理っぽくしてくれているんですねってことです。

コールバック関数とは軽く説明すると、関数の引数に別の関数を呼び出すのです。

ある関数Aに対して引数として関数Bを渡し、関数Bを関数Aの内で呼び出すようにプログラミングするとします。この時の関数Bをコールバック関数と言います。

ざっくり説明しましたが、本当は、イベントループとか、I/O多重化とかロールベルIOメカニズムとかEポールとか難しい用語がたくさん出てきて説明するために理解しようと頑張りましたが・・・今の私の勉強レベルでは理解が難しくついていけません。

いやこういうことなんだよって説明してくださる受講生の方がいましたらぜひ追記お願いします。

まとめ

同期処理は一つのコアで頑張って処理する

非同期処理は、スレッドやプロセスを複数使って同時に複数の処理を実行する

並列処理は、並列に処理するから並列処理!

並行処理は、複数の処理を同時に実行しているから並行処理

並列処理は、一つの処理だと待ち時間がもったいないよね、別のCPUのコアで処理させたほうが便利だよね的な意味で生まれた。

マルチプロセスは、プロセス間のデータ共有が難しいがメモリ空間の排他制御しなくていいメリットがあるが、やはりコンテキストスイッチのコストが大きい

マルチスレッドは、同一スレッドのデータ共有がしやすいけど、メモリ空間の排他制御しないといけない

C10K問題を解決するためにイベント駆動型モデルが生まれた

技術は不便だと思うことから生まれてくるのです。

そうした歴史背景を理解すれば自然と理解につながってくると思います。

今回の授業は2時間を超えてボリュームたっぷりだったのですが、あまりに素晴らしすぎて私にはまとめきれませんでした。

気になる方は授業動画を見ましょう!

追記

画像がないとわかりづらいと思うので近日中に追記させていただきます

宣伝

RareTECHに興味のある方は、こちらのHPをご覧ください。
無料体験授業も行っていますので、ぜひご参加くださいね。 RareTECH