아래 코드는 ‘예기치 않은 이동’이라는 컴파일 오류를 제공합니다.
x := go doSomething(arg)
func doSomething(arg int) int{
...
return my_int_value
}
고 루틴을 사용하지 않고 정상적으로 함수를 호출하면 반환 값을 가져올 수 있습니다. 또는 채널 등을 사용할 수 있습니다.
내 질문은 고 루틴에서 이와 같은 반환 값을 가져올 수없는 이유입니다.
답변
엄격한 대답은 그렇게 할 수 있다는 것입니다. 그것은 아마도 좋은 생각이 아닐 것입니다. 이를 수행하는 코드는 다음과 같습니다.
var x int
go func() {
x = doSomething()
}()
이것은 계산 doSomething()
한 다음 결과를에 할당 할 새로운 고 루틴을 생성합니다 x
. 문제는 : x
원래 고 루틴에서 어떻게 사용할 것인가? 경합 상태가 없도록 생성 된 고 루틴이 완료되었는지 확인하고 싶을 것입니다. 하지만 그렇게하려면 고 루틴과 통신 할 방법이 필요합니다. 그렇게 할 수있는 방법이 있다면 값을 다시 보내는 데 사용하는 것이 어떻습니까?
답변
고 루틴에서 반환 값을 가져와 변수에 할당 할 수없는 이유는 무엇입니까?
goroutine을 (비동기 적으로) 실행하고 함수에서 반환 값을 가져 오는 것은 본질적으로 모순되는 작업입니다. 당신이 말할 때 go
당신은 의미 “비동기 적으로 그것을”또는 간단 : “할 일에 이동 완료 될 기능 실행을 위해 기다릴 수 없다”. 그러나 함수 반환 값을 변수에 할당하면 변수 내에이 값이있을 것으로 예상됩니다. 그래서 당신이 그렇게 할 때 당신은 x := go doSomething(arg)
“계속, 함수를 기다리지 마세요! Wait-wait-wait! 반환 된 값이 필요합니다. x
바로 아래 줄의 var에서 접근 가능 합니다!”
채널
고 루틴에서 값을 가져 오는 가장 자연스러운 방법은 채널입니다. 채널은 동시 고 루틴을 연결하는 파이프입니다. 한 고 루틴에서 채널로 값을 보내고 다른 고 루틴이나 동기 함수에서 해당 값을 수신 할 수 있습니다. 다음을 사용하여 동시성을 깨지 않고 goroutine에서 값을 쉽게 얻을 수 있습니다 select
.
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
// Await both of these values
// simultaneously, printing each one as it arrives.
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
예제는 Go By Example에서 가져온 것입니다.
CSP 및 메시지 전달
Go는 대체로 CSP 이론을 기반으로 합니다. 위의 순진한 설명은 CSP 측면에서 정확하게 설명 될 수 있습니다 (물론 범위를 벗어난 것으로 생각합니다). 적어도 RAD이기 때문에 CSP 이론에 익숙해지는 것이 좋습니다. 이 짧은 인용문은 생각의 방향을 제시합니다.
이름에서 알 수 있듯이 CSP 는 독립적으로 작동 하고 메시지 전달 통신을 통해서만 서로 상호 작용 하는 구성 요소 프로세스 측면에서 시스템을 설명 할 수 있습니다 .
컴퓨터 과학에서 메시지 전달은 메시지를 프로세스에 보내고 프로세스 및 지원 인프라에 의존하여 실행할 실제 코드를 선택하고 호출합니다. 메시지 전달은 프로세스, 서브 루틴 또는 함수가 이름으로 직접 호출되는 기존 프로그래밍과 다릅니다.
답변
go
키워드 의 아이디어는 doSomething 함수를 비동기 적으로 실행하고 결과를 기다리지 않고 현재 고 루틴을 계속하는 것입니다. 마치 Bash 쉘에서 명령 뒤에 ‘&’가있는 명령을 실행하는 것과 같습니다. 하고 싶다면
x := doSomething(arg)
// Now do something with x
그런 다음 doSomething이 완료 될 때까지 차단할 현재 고 루틴이 필요합니다. 그렇다면 현재 고 루틴 에서 doSomething 을 호출하지 않는 이유 는 무엇입니까? 다른 옵션 (예 : doSomething은 현재 고 루틴이 값을받는 채널에 결과를 게시 할 수 있음)이 있지만 단순히 doSomething을 호출하고 그 결과를 변수에 할당하는 것이 훨씬 간단합니다.
답변
Go 제작자가 선택한 디자인입니다. -이의 I / O 작업이 비동기의 값을 표현하는 추상화 / API를 훨씬이다 promise
, future
, async/await
, callback
, observable
-, 등이 추상화는 / API를 본질적으로 스케줄링의 단위에 연결되어 코 루틴은 – 이러한 추상화 / API를 지시하는 방법 코 루틴 ( 또는 더 정확하게는 그들로 표현되는 비동기 I / O의 반환 값)을 구성 할 수 있습니다 .
Go는 비동기 I / O 작업의 반환 값을 나타내는 추상화 / API로 메시지 전달 (일명 channels )을 선택했습니다 . 물론 고 루틴과 채널은 비동기 I / O 작업을 구현할 수있는 구성 가능한 도구를 제공합니다.