Виловлювання повернутих значень із програмних програм


82

У наведеному нижче коді дається помилка компіляції, яка говорить "несподіваний вихід":

x := go doSomething(arg)

func doSomething(arg int) int{
    ...
    return my_int_value
}

Я знаю, я можу отримати значення повернення, якщо нормально викликати функцію, не використовуючи goroutine. Або я можу використовувати канали тощо.

Моє питання полягає в тому, чому неможливо отримати таке значення повернення з програми.


7
ви можете використати канал, щоб повернути його
rogerdpack

чому це дозволяє мати повернене значення для
горутини

1
@rogerdpack, який вимагає змінити API будь-якої функції, яку ви використовуєте. тож вам може знадобитися функція обгортки, якщо вона не ваша
Девід Калланан

Відповіді:


66

Точна відповідь полягає в тому, що ви можете це зробити. Це, мабуть, не дуже гарна ідея. Ось код, який міг би це зробити:

var x int
go func() {
    x = doSomething()
}()

Це призведе до появи нової програми, яка обчислить, doSomething()а потім призначить результат x. Проблема в тому, як ви збираєтеся використовувати xоригінальний горутин? Ви, мабуть, хочете переконатися, що породжена горутина виконана з нею, щоб у вас не було перегонів. Але якщо ви хочете це зробити, вам знадобиться спосіб зв’язку з горутиною, і якщо у вас є спосіб це зробити, чому б просто не використати його, щоб відправити значення назад?


6
Ви можете додати групу очікування, щоб переконатися, що закінчили, і дочекатися її. Але як ви сказали, це просто не спосіб це зробити, це канал.
Not_a_Golfer

1
Це ні return, це assignмент
Нідхін Девід

94

Чому неможливо отримати значення, що повертається, із програми, що присвоює її змінній?

Запуск goroutine (асинхронно) та отримання поверненого значення з функції є по суті суперечливими діями. Коли ви говорите, goви маєте на увазі "робіть це асинхронно" або навіть простіше: "Продовжуйте! Не чекайте завершення виконання функції". Але коли ви призначаєте функцію, що повертає значення змінної, ви очікуєте, що це значення буде в змінній. Отже, коли ви робите це, x := go doSomething(arg)ви говорите: "Продовжуйте, не чекайте функції! Чекайте-чекайте-чекайте! Мені потрібно, щоб повернене значення було доступне у xvar прямо в наступному рядку нижче!"

Канали

Найбільш природний спосіб отримати значення з горутини - це канали. Канали - це труби, які з'єднують одночасно горутини. Ви можете надсилати значення в канали з однієї програми та отримувати ці значення в іншу програму або в синхронній функції. Ви можете легко отримати значення з програми, що не порушує паралелізм, використовуючи 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 (хоча я вважаю, що це виходить за межі питання). Я настійно рекомендую ознайомитися з теорією CSP хоча б тому, що це RAD. Ці короткі цитати дають напрямок мислення:

Як випливає з назви, CSP дозволяє описувати системи з точки зору компонентних процесів, які працюють незалежно та взаємодіють між собою виключно за допомогою передачі повідомлень .

В інформатиці передача повідомлень надсилає повідомлення процесу і покладається на процес та підтримуючу інфраструктуру для вибору та виклику фактичного коду для запуску. Передача повідомлень відрізняється від звичайного програмування, коли процес, підпрограма або функція викликається безпосередньо за іменем.


9

Ідея goключового слова полягає в тому, що ви запускаєте функцію doSomething асинхронно і продовжуєте поточну програму, не чекаючи результату, начебто виконання команди в оболонці Bash із знаком "&" після неї. Якщо ви хочете зробити

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

тоді вам потрібно заблокувати поточну програму, поки doSomething не закінчиться. То чому б просто не викликати doSomething у поточній програмі? Є й інші варіанти (наприклад, doSomething може опублікувати результат на каналі, з якого поточна програма отримує значення), але просто виклик doSomething і присвоєння результату змінній очевидно простіше.


0

Це вибір дизайну від творців Go. Там цілі багато абстракцій / API для подання значення асинхронних операції введення / виведення - promise, future, async/await, callback, observableі т.д. Ці абстракції / API , які по своїй природі пов'язані з одиницею планування - співпрограми - і ці абстракції / API , диктують , як співпрограми ( або точніше повернене значення асинхронного вводу-виводу, представлене ними) може бути складене .

Перейдіть, вибравши передачу повідомлень (так звані канали ) як абстракцію / API, щоб представити повернене значення асинхронних операцій вводу-виводу. І, звичайно, програми та канали дають вам компонуючий інструмент для реалізації асинхронних операцій вводу-виводу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.