3장 go 동시성 구성 요소
고루틴
- 가장 기본적인 단위(main도 하나의 고루틴이다)
- 고루틴은 os스레드가 아니고, 그린스레드도 아니고, coroutine이라고 불리는 더 높은 추상화이다.
- hw 스레드: 물리 cpu를 가상화하여서 2개의 코어로 만드는것. hyper-threading기능이라고 생각하면 됨
- os 스레드(native 스레드): 일반적으로 우리가 생각하는 core의 메모리를 공유하는 프로세스, cpu스케쥴링의 단위
- 그린 스레드: 유저스레드 to os스레드가 many to one으로 연결되는 형태
- 코루틴: 동시에 실행되는 서브루틴으로 비선점적인 루틴을 의미한다.
- 고루틴은 go 런타임과 긴밀하게 통합되어 있어서, 잘 효율적으로 실행됨
- m:n 스케줄러를 구현한것으로 m개의 그린 슬레드를 n개의 os스레드에 매핑하는 것. 그후 고루틴은 스레드에 스케줄링된다.
- fork-join모델을 따른다.
- 단순한게 time sleep을 하는건 레이스 컨디션을 만들뿐이다.
- 고루틴은 자신이 생성된 곳과 동일한 주소 공간에서 실행된다.고루틴들은 순차적으로 실행된다는 보장이 없으며, 그것을 위해서 여러가지 고려를 하여서 코드를 작성해야된다.
- 고루틴이 실행되기전 이미 for loop는 종료되었고, 그 메모리 주소만 공유되고 있었기때문에 고루틴이 메모리에 접근할 수 있도록 메모리를 힙으로 옮겼고, c만 출력되게 된다.
- 가비지컬렉터는 버려진 고루틴을 회수하기위해서 어떠한 것도 하지않는다.
- 고루틴은 엄청 싸다. 70만개에 메모리 2GB
- 컨텍스스위칭도 엄청 싸다. 소프트웨어 스위칭이기때문에 저장할것을 선택할 수 있기 때문에.
var wg sync.WaitGroup
for _, salutation := range[]string{"a","b","c"}
wg.Add(1)
go func (){
defer wg.Done
fmt.Println(salutation) }()
wg.Wait()
결과값 c c c
sync 패키지(기존 메모리 공유 방식 지원)
- waitGroup
- 동시에 수행된 연산의 결과를 신경 쓰지 않는경우
- 결과를 수집할 다른 방법이 있는 경우 동시에 수행될 연산 집합을 기다릴때
- 동시에 실행해도 안전한 카운터(add(+), done(-), wait)
- mutex와 rwmutex
- mutex는 상호 배제 mutual exclusion, 임계 영역을 보호
- 규칙을 만들어 메모리를 공유
- (lock, unlock) lock을하고 defer unlock을 호출
- 임계 영역은 프로그램의 병목지점이다.
- rwmutex(read write), 좀더 메모리를 제어 할 수 있는 것
- 쓰기일때는 잠금, 읽기만 있으면 몇개든 ok
- cond
- 고루틴들이 대기하거나, 어떤 이벤트의 발생을 알리는 집결 지점
- 고루틴에서 신호를 기다리기
- 가장쉬운 방법은 무한루프를 사용
- 신호를 받을때까지 슬립하고 자신의 상태를 확인하는 방식
- (L.lock, L.unlock, signal, broadcast)
- signal은 fifo로 가장 오래기다른 고루틴에게 신호전달
- groadcast는 대기중인 모든 고루틴에게 동시에 신호전달
- once
- once의 do 메소드는 once자체를 단 1번마 실행하게 만든다.
- do의 호출함수 기준이 아닌 do의 호출 횟수가 중요하다.
- pool
- 데이터베이스 연결같이 비용이 많이 드는 것의 생성수를 제한해 고정된 수만 생성하도록 하는 기능
- (get put)
- get을 통해서 사용가능한 인스턴수 수를 1개 감소시간다.
- put을 통해서 사용가능한 인스턴수 수를 1개 증가시킨다.
- go에는 가비지컬렉터가 있으므로, 인스턴스화된 객체는 자동으로 정리된다.
- 높은 처리가 필요한 네트워크 서버를 작성하는 경우 필요하다.
채널
- go동기화의 기본 요소
- 메모리 접근을 동기화
- 고루틴 간에 정보를 전달할 때 가장 적합
- 변수의 마지막은 stream으로
- 채널 선언 및 인스턴트화 2스텝
- 데이터 형을 선언, 주소값 전달
- := make(chan int)와 같은 형태로 한번에 선언 가능
- value, ok := <-stringStream(value는 전달된 값 ok는 open channel은 true, close channel은 false)
- 두번째 반환값은 channel의 close여부를 판별하기위함, close일때 계속 defaul값이들어온다.
- 채널을 닫는것또한 여러개의 수신 채널에 동시에 신호를 보내는 방법 중 하나이다.
- 버퍼링된 채널(버퍼에 쌓아놓는다.) 다쌓이면 송신채널은 대기하게 된다.
- 버퍼링 채널은 성급한 최적화가 될 수 있고, 데드락의 원인이 될 수 있다.
- 채널의 소유권을 할당하는걸 가장 먼저 해야된다.
- 채널 인스턴트화/ 다른 고루틴에게 소유권을 넘긴다/ 채널을 닫는다/ 캡슐화한다
- 채널 소비자
- 언제 채널 닫히는지 아는것(두번째 리턴값)/ 대기가 발생하면 책임있게 처리하는 것
select 구문
- 채널을 하나로 묶는 방식
-
- 채널 읽기와 쓰기를 동시에 고려
- 준비된 채널이 없을 경우 select문 전체가 중단 대기된다.
- case 구문의 집합에 대한 균일한 의사 무작위 선택, 완전 랜덤
- 동시에 case에 입력이 들어가면? 그럼 모두 랜덤 선택case에대해서 순차적으로 테스트되지않으며, 조건이 충족되지않는다고 다음으로 넘어가지 않는다.
- 모든 채널이 차단되어 대기하는 경우 대비해 default절을 허용한다.
- select{} 무한대기
var c <- chan int
select {
case <-c:
case<-time.After(1*time.Second):
fmt.print("timed out")
}
결과: 1초만 기다리고 종료됨'프로그래밍 > golang' 카테고리의 다른 글
| concurrency in go(4) (0) | 2023.01.06 |
|---|---|
| concurrency in go(3) (0) | 2023.01.04 |
| concurrency in go(1) (0) | 2022.12.31 |
| google go style guide 공부 (0) | 2022.12.12 |
| golang 팀내 테스트 코드 작성 (0) | 2022.11.20 |