Go Scheduler Structure


### G, M, P로 구성
> - g - goroutine

> 
> [Go g struct](https://texonom.com/go-g-struct-01edc60ab74543979c0a003e0945ddf6)
> 

> - m - thread

> 
> [Go m struct](https://texonom.com/go-m-struct-dec57bc7eb4d4b4d9214f458c777f56d)
> 

> - p - context

> 
> [Go p struct](https://texonom.com/go-p-struct-d570679fd68647169ac894ee440759b5)
> 


> ![](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F6ace99d7-e70f-4fc3-8243-82b643fd9abe%2FUntitled.png?table=block&id=6a6e20a3-aa59-46ed-8c8d-9dda1ef2c95a&cache=v2)

> ### 일반
> context인 P의 갯수는 `GOMAXPROCS `환경 변수로 설정
> `runtime.GOMAXPROCS()` 로 조절 가능
> 대부분 프로그램을 처음 실행할 때에 이 값을 설정
> 회색 큐 (`runqueues`) 로 나타나는 부분은 아직 실행중이지는 않지만 스케쥴될 준비가 되어있는 고루틴 - P(context)에 종속




> ![](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fb6f5083b-75fb-4134-8468-4b235415fa67%2FUntitled.png?table=block&id=28ee3467-3f91-4fc8-99ad-9197a7337454&cache=v2)
> Go의 스케쥴러는 계속해서 멈추지 않고 스케쥴링을 할 수 있도록 syscall이 발생한 고루틴을 다른 스레드로 넘겨(hand off) 모든 고루틴이 정상적으로 작동할 수 있도록 보장

> ### syscall이 발생했을 때 (blocking)
> 이후에 syscall 처리가 끝난 고루틴은 잠시 넘겼던 P(context)를 다시 찾아와서 붙게 되거나 global runqueues 에 들어감Go의 스케쥴러는 syscall이 발생한 고루틴을 독립적으로 처리하도록 하면서 나머지 context는 다른 스레드(위 이미지에서 M1)로 넘김
> 실행중인 스레드가 blocking 되었을 때 다른 스레드로 현재 상태(state)를 그대로 넘겨 지속적으로 처리를 할 수 있도록 보장하기 위해 M에 P(context)가 붙는다
> P(context)가 구현되었고 이는 GOMAXPROCS값이 1이더라도 Go가 멀티스레드로 동작하는 이유입니다



과거 Go에는 global runqueues가 있었지만 lock-release 문제로 P마다 own runqueues를 할당하는 방식으로 변경
고루틴의 CS는 다음 시점에서 이루어짐
- unbuffered 채널에 접근할 때(읽거나 쓸 때)
- 시스템 I/O가 발생했을 때
- 메모리가 할당되었을 때
- `time.Sleep()` 코드 실행(python asyncio에서 `asyncio.sleep()`을 이용해 yield하는 것과 유사합니다)
- `runtime.Gosched()` 코드 실행


> [Goroutines vs Threads](https://tech.ssut.me/goroutine-vs-threads/)




