Golang의 동시성 소개

동시성 문제를 처리할 때 추상화 스택(기계, 프로세스, 스레드, 하드웨어 구성 요소 등) 아래로 이동할 때 추론하기가 더 어렵습니다. 대부분의 프로그래밍 언어는 스레드를 최고 수준의 추상화로 사용합니다. 다행스럽게도 그 위에 빌드하고 고루틴을 소개합니다.

“Share memory by communicating, don’t communicate by sharing memory.” - One of Go’s mottos



Golang은 sync 패키지에서 전통적인 잠금 메커니즘을 제공하지만 철학은 "통신을 통한 메모리 공유"를 선호합니다. 따라서 Golang은 Goroutine이 서로 통신할 수 있는 매체로 채널을 도입합니다.

//goroutine
go func() { 
    // do some work
}()

//channel
dataStream := make(chan interface{})



포크 조인 모델



Go가 따르는 포크 조인 동시성 모델

이 동시성 모델은 Golang에서 사용됩니다. 언제든지; 자식 Goroutine은 부모와 동시 작업을 수행하기 위해 분기될 수 있으며 어느 시점에서 다시 합류할 것입니다.

모든 Go 프로그램에는 메인 고루틴이 있습니다. 주요 항목은 자식보다 일찍 종료될 수 있습니다. 결과적으로 자식 Goroutine이 완료할 기회가 있는지 확인하려면 조인 포인트가 필요합니다.

채널



채널에는 다음 속성이 있습니다.
  • 고루틴 안전(여러 고루틴이 경쟁 조건 없이 공유 채널에 액세스할 수 있음)
  • FIFO 대기열 의미 체계

  • 채널은 항상 2개의 값을 반환합니다. 1은 반환된 개체, 1은 상태입니다(true는 유효한 개체를 의미하고, false는 이 채널에서 더 이상 값이 전송되지 않음을 의미합니다.)

    intStream := make(chan int)
    close(intStream)
    integer, ok := <- intStream 
    fmt.Printf("(%v): %v", ok, integer) // (false): 0
    
    


    채널 방향




    var dataStream <-chan interface{} //read from only channel
    var dataStream chan<- interface{} // write to only channel
    var dataStream chan interface{} // 2 ways
    
    


    채널 용량



    채널의 기본 용량은 0(버퍼링되지 않은 채널)입니다. 비어 있는 상태에서 읽거나 전체 채널에 쓰는 것이 차단됩니다.

    c := make(chan int,10) //buffered size of 10
    c := make(chan int) //unbuffered channel
    
    


    if a buffered channel is empty and has a receiver, the buffer will be bypassed and the value will be passed directly from the sender to the receiver.



    고루틴에 대한 채널 동작



    고루틴이 채널에서 읽거나 쓸 때 채널 상태에 따라 다양한 동작이 발생할 수 있습니다.

    intStream := make(chan int) // 0 capacity channel
    
    go func() {
        defer close(intStream) 
        for i:=1; i<=5; i++{
          intStream <- i
      }
    }()
    
    //range from a channel
    for integer := range intStream { // always blocked until the channel is closed
        fmt.Printf("%v ", integer)
    }
    
    


    아래 표에는 모든 동작이 요약되어 있습니다.


    운영
    채널 상태
    결과


    읽다

    차단하다

    열기 및 비어 있지 않음


    열기 및 비우기
    차단하다

    닫은
    [기본값], false

    쓰기 전용
    컴파일 오류

    쓰다

    차단하다

    열기 및 전체
    차단하다

    열기 및 가득 차지 않음
    값 쓰기

    닫은panic
    받기만
    컴파일 오류

    닫다
    panic
    열기 및 비어 있지 않음
    채널을 닫습니다. 후속 읽기는 채널이 비어 있을 때까지 값을 성공한 다음 기본 값을 읽습니다.

    열기 및 비우기
    채널을 닫습니다. 읽기는 기본값을 생성합니다.

    닫은panic
    받기만
    컴파일 오류


    동작이 clomplex이므로 강력하고 확장 가능한 프로그램을 만들 수 있는 방법이 있어야 합니다.

    채널 작업 시 강력하고 확장 가능한 방식



    다음은 1가지 제안 방법입니다.
  • 최대 1개의 고루틴이 채널 소유권을 갖습니다. 관리할 수 있으려면 채널 소유권이 작아야 합니다.
  • 채널 소유자에게 쓰기 액세스 권한이 있습니다(chan←).
  • 소비자에게는 읽기 전용 보기만 있습니다( ←chan ).

  • 1 채널 소유자

    책임:
  • 초기화 채널
  • 채널에 쓰거나 다른 고루틴에 소유권을 넘기십시오.
  • 채널 닫기
  • 채널을 리더 채널로 캡슐화 및 노출

  • 2채널 소비자

    책임 :
  • 채널이 닫힐 때 처리
  • 채널에서 읽을 때 차단 동작 처리

  • chanOwner := func() <-chan int { 
        resultStream := make(chan int, 5) //init
        go func() {
            defer close(resultStream) // close
            for i:=0;i<=5;i++{ 
                resultStream <- i // write
        } }()
        return resultStream // read channel returned
    }
    
    resultStream := chanOwner()
    for result := range resultStream {
      fmt.Printf("Received: %d\n", result)
    }
    fmt.Println("Done receiving!")
    
    


    참조:

    Go의 동시성: 개발자를 위한 도구 및 기술 - Katherine Cox-Buday 저서

    좋은 웹페이지 즐겨찾기