同期・非同期処理をざっくり俯瞰

# はじめに

「同期処理、非同期処理」についての勉強会用に資料として作成した。
資料の公開場所としてQiitaがちょうどよかったので選択した。

本記事ではタイトルにある同期・非同期に加え、

- 並列処理
- 並行処理

を加えた4点をいくつかの技術と合わせて概念と特徴、それぞれの用途をつかむことを目指す。

# 用語説明

始めにそれぞれをどういうものか説明をしていきたいのだが、すでに素晴らしい記事が存在していた。
下記を参考にされたい。

https://qiita.com/kyabetsuda/items/384a57ff6b7250de40ad

https://zenn.dev/tide/articles/concurrent-parallel-sync-async

とはいえ投げっぱなしもどうかと思うので、自分なりにどういうものかを書いてみる。

## 同期・非同期

処理を待つ・待たないの違い。

### 同期処理
プログラムの実行順序に沿って実行される。私の想定する一般的なコードなら書いた順番で実行がされると言い換えてもよい。
なんのページ開いても書いてあるのでn番煎じとなるが、途中に重たい処理が入ってもその処理が終わるまで待つため、実行時間はすべての処理の総和となる。

### 非同期処理

同期処理と異なり、処理が終わるのを待たずに先に進む。
これにより、これまで同期的にやる必要のあった重たい処理を待たなくて済むようになった。
とはいえ非同期処理の結果が欲しい場合もあり、この非同期の結果を取得したいときに用いられるデザインパターンがある。後述。

https://qiita.com/komaitaira/items/54b5c9f3d54695974704

## 並列・並行処理

下記の図がわかりやすい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299962/a78c269c-75a8-922f-19f2-d52fb8dd25f3.png)

[マルチスレッドと並行処理をわかりやすく説明します](https://blog.framinal.life/entry/2020/04/05/204055)

非同期処理の実現方法として並列処理、並行処理が存在する。

https://qiita.com/4_mio_11/items/7f418ca661d9f5a2a39d

### 並列処理

実行単位はプロセスだったりスレッドだったりとあるが、同時に別処理が動く。

### 並行処理

あるタイミングにおいては1つの処理しかしていないが、複数の処理を高速に切り替えながら同時にやっているように見せる。
JavaScriptには非同期処理があるが、JavaScriptはシングルスレッドで動作しているため並行処理にあたる。

https://qiita.com/klme_u6/items/ea155f82cbe44d6f5d88

https://engineering.mercari.com/blog/entry/20220128-3a0922eaa4/

# マルチプロセス・マルチスレッド

上記で実行単位としてプロセスやスレッドがあると記載した。それぞれ複数立ち上げることをマルチプロセス・マルチスレッドという。
マルチスレッド、マルチプロセスの概念図は以下の通り。

- マルチスレッド

プロセスが存在し、プロセスからスレッドが切られる。
各スレッドは同じメモリ空間を共有できるので、スレッドどうしのデータ共有が楽。だけども排他制御は考える必要があって面倒。
スレッドの切り替えがボトルネックとなり、プロセスが死んだらスレッドも根こそぎ死ぬ。

```mermaid
graph LR
  プロセス-->スレッド1;
  プロセス-->スレッド2;
```

- マルチプロセス

マスタープロセスがプロセスを分割する。
プロセス独自のメモリ空間を持っているからマルチスレッドみたいに排他制御を考えなくていい。でもデータの共有は面倒。
プロセスは[AWS lambda](https://aws.amazon.com/jp/lambda/)をイメージするとわかりやすいかも。
プロセスを切るのがボトルネック。

```mermaid
graph LR
  マスタープロセス-->プロセス1;
  マスタープロセス-->プロセス2;
  プロセス1-->スレッド1;
  プロセス2-->スレッド2;
  プロセス2-->スレッド3;
```

- 使いどころさん

スレッドやプロセスを切らない方が速いこともある。
分割しているんだから速いんじゃないの？
そう思われるのも無理はないが、プロセスやスレッドを切るのにかかる時間がボトルネックとなって遅くなる場合があるためだ。

https://qiita.com/4_mio_11/items/f97a5ac374bd56b6bc22

https://qiita.com/4_mio_11/items/4987be19c31df70bbf28

# フューチャーパターン

非同期処理の部分で述べた、非同期処理の結果を受け取りたいときに用いられるデザインパターン。

https://ja.wikipedia.org/wiki/Future_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3

フューチャーパターンの概念理解にはこれがわかりやすい

https://zenn.dev/yucatio/articles/14fe4507fde3d4

JavaScriptのPromiseやgoroutineのchannelがFutureオブジェクトに当たる認識で、何らかの非同期処理の結果を得る引換券となる。

まずはgoroutineで見てみよう。

```go
package main

import "fmt"

func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // send sum to c
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // receive from c

	fmt.Println(x, y, x+y)  // -5 17 12
}
```

`go sum(/** param */)` でスレッドを切っており、`x, y := <-c, <-c` でスレッドでの結果を得ている。
ここでの変数 `c` はスレッドの結果が格納される変数として定義されており、mainスレッドから見れば別スレッドでの処理結果の引換券としてみることができる。

https://go-tour-jp.appspot.com/concurrency/2

https://zenn.dev/mikankitten/articles/6344d71f4f4920

https://qiita.com/ta1m1kam/items/fc798cdd6a4eaf9a7d5e

次にJavaScriptのfetch APIを見てみる。

```js
const res = await fetch('https://example.com');
const data = await res.json();
data
```

Top level awaitは無視していただいて、ここでもfetchでAPIからの取得結果を受け取るresがある。
fetchの返却値はPromise型であり、awaitしなければPromise型として中身が入っていない状態で出力される。
それはPromise型がFutureオブジェクトだからであり、牛丼ができてから引換券と交換する処理として `await` や `.then()` で受け取る必要がある。

https://jsprimer.net/use-case/ajaxapp/http/

https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch

# イベント駆動

あるイベントが起きたタイミングで任意の処理を発火させる。
[AWS lambda](https://aws.amazon.com/jp/lambda/)であれば[EventBridge](https://aws.amazon.com/jp/eventbridge/)と合わせて任意の時間をトリガーに処理を動かせるし、[API Gateway](https://aws.amazon.com/jp/api-gateway/)と組み合わせればHTTP METHODでのリクエストがきたときに紐づくlambdaの処理を動かせる。

EventBridge - lambda

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299962/70b128d9-5696-03d2-c8ff-762690ef99a6.png)

api gateway - lambda

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299962/856ae112-2e4d-ca08-7c3c-ba23ab68fefc.png)

Webでは[EventTarget.addEventListener()](https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener)が有名だろう。
これはクリックイベントや読み込み完了イベント、スクロールイベントなどを検知して任意の処理を動作させる。

```js
// clickで発火
htmlElement.addEventListener('click', (event) => {/** ... */});
// load時
window.addEventListener('load', (event) => {/** ... */});
// スクロール
document.addEventListener('scroll', (event) => {/** ... */});
```

# バッチ処理

いろんな箇所で一括処理というような書き方がされており、実際その通りなのだが、ここでは別プロセスを切ってそこで大きな処理を行うものだと思ってもらいたい。

https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%83%E3%83%81%E5%87%A6%E7%90%86

AWS lambdaを使ってやるんじゃダメなん？　上記しているイベント駆動的なものでしょ？
その通りなのだが、lambdaには時間制限があり、時間のかかる一括処理には向かない。
そこで[ECS](https://aws.amazon.com/jp/ecs/)を用いたバッチ処理を紹介する。

雑な絵だが、ECSでAPIを立ち上げており、何らかの重たい処理を行いたいとする。

![aaa.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299962/91a9e7e9-1965-3306-1941-05dcfc4a6fb0.png)

しかしAPIの中で行うとフロントを待たせてしまいUXを著しく損なう。また、何らかの重たい処理は即時性は求めず、バッチ処理で行いたい。
となったとき、タスクAでAPI処理が行われ、タスクBをバッチ処理として立ち上げる。
タスクAはタスクBをrun_taskして満足しレスポンスを返却。
タスクBは非同期的にバッチ処理が行われる。
これはマルチプロセスに近い。

また、上記は最初ECSタスクが立ち上がっていないコールドスタンバイでタスクを立ち上げるまで時間がかかった。
ホットスタンバイの一例として、[Celery](https://docs.celeryq.dev/en/stable/getting-started/introduction.html)が挙げられる。

これまた雑な絵になってしまうが、バッチ処理で用いるECSタスクは立ち上げたまま、[ElastiCache for Redis](https://aws.amazon.com/jp/elasticache/redis/)をブローカーとして、バッチ処理の指示を送る。

![bbb.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299962/2d083375-2610-ccbe-724a-9a05bd1f9366.png)

大まかな概要はrun_taskするのと大して変わらないが、ホットスタンバイかコールドスタンバイで異なる。

# おわりに

同期・非同期処理を実際の使用方法が各機能とともに浅く広く舐めた。

# 発展

TypeScriptが得意なのでフロント厚め

- [なるほどUnixプロセス](https://tatsu-zine.com/books/naruhounix)
- [イベントループと TypeScript の型から理解する非同期処理](https://zenn.dev/mizchi/articles/understanding-promise-by-ts-eventloop)
- [RxJS](https://rxjs.dev/)
- [RxのHotとColdについて](https://qiita.com/toRisouP/items/f6088963037bfda658d3)
- [std::thread](https://cpprefjp.github.io/reference/thread/thread.html)
- [Using Threads to Run Code Simultaneously](https://doc.rust-lang.org/book/ch16-01-threads.html)