IT story

고 루틴에서 반환 값 잡기

hot-time 2021. 1. 6. 20:24
반응형

고 루틴에서 반환 값 잡기


아래 코드는 '예기치 않은 이동'이라는 컴파일 오류를 제공합니다.

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 이론에 익숙해지는 것이 좋습니다. 이 짧은 인용문은 생각의 방향을 제시합니다.

As its name suggests, CSP allows the description of systems in terms of component processes that operate independently, and interact with each other solely through message-passing communication.

In computer science, message passing sends a message to a process and relies on the process and the supporting infrastructure to select and invoke the actual code to run. Message passing differs from conventional programming where a process, subroutine, or function is directly invoked by name.


The idea of the go keyword is that you run the doSomething function asynchronously, and continue the current goroutine without waiting for the result, kind of like executing a command in a Bash shell with an '&' after it. If you want to do

x := doSomething(arg)
// Now do something with x

then you need the current goroutine to block until doSomething finishes. So why not just call doSomething in the current goroutine? There are other options (like, doSomething could post a result to a channel, which the current goroutine receives values from) but simply calling doSomething and assigning the result to a variable is obviously simpler.


It's a design choice by Go creators. There's a whole lot of abstractions/APIs to represent the value of async I/O operations - promise, future, async/await, callback, observable, etc. These abstractions/APIs are inherently tied to the unit of scheduling - coroutines - and these abstractions/APIs dictate how coroutines (or more precisely the return value of async I/O represented by them) can be composed.

Go chose message passing (aka channels) as the abstraction/API to represent the return value of async I/O operations. And of course, goroutines and channels give you a composable tool to implement async I/O operations.

ReferenceURL : https://stackoverflow.com/questions/20945069/catching-return-values-from-goroutines

반응형