Нет результатов от goroutine в Go

Пока SayHello() выполняется так, как ожидалось, goroutine ничего не печатает.

package main

import "fmt"

func SayHello() {
    for i := 0; i < 10 ; i++ {
        fmt.Print(i, " ")
    }
}

func main() {
    SayHello()
    go SayHello()
}

Ответ 1

Когда ваша функция main() завершается, ваша программа заканчивается. Это не дожидается завершения других гортанов.

Цитата из Go Language Specification: Выполнение программы:

Выполнение программы начинается с инициализации основного пакета и последующего вызова функции main. Когда возвращается эта функция, программа завершает работу. Он не ждет завершения других (не main) goroutines.

Подробнее см. этот ответ.

Вы должны сообщить своей функции main(), чтобы функция SayHello() запускалась как goroutine для завершения. Вы можете синхронизировать их с каналами, например:

func SayHello(done chan int) {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
    if done != nil {
        done <- 0 // Signal that we're done
    }
}

func main() {
    SayHello(nil) // Passing nil: we don't want notification here
    done := make(chan int)
    go SayHello(done)
    <-done // Wait until done signal arrives
}

Другой альтернативой является сигнализация завершения, закрывая канал:

func SayHello(done chan struct{}) {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
    if done != nil {
        close(done) // Signal that we're done
    }
}

func main() {
    SayHello(nil) // Passing nil: we don't want notification here
    done := make(chan struct{})
    go SayHello(done)
    <-done // A receive from a closed channel returns the zero value immediately
}

Примечания:

В соответствии с вашими изменениями/комментариями: если вы хотите, чтобы 2 выполняемые функции SayHello() печатали "смешанные" цифры случайным образом: у вас нет гарантии наблюдать такое поведение. Опять же, см. вышеупомянутый ответ для получения более подробной информации. Модель памяти Go гарантирует, что определенные события происходят раньше других событий, у вас нет гарантии, как выполняются 2 параллельных goroutines.

Вы можете экспериментировать с ним, но знаете, что результат не будет детерминированным. Сначала вы должны разрешить выполнение нескольких активных goroutines с помощью:

runtime.GOMAXPROCS(2)

И во-вторых, вы должны сначала запустить SayHello() в качестве goroutine, потому что ваш текущий код сначала выполняет SayHello() в главном goroutine, и только после того, как он закончит, запустите другой:

runtime.GOMAXPROCS(2)
done := make(chan struct{})
go SayHello(done) // FIRST START goroutine
SayHello(nil) // And then call SayHello() in the main goroutine
<-done // Wait for completion

Ответ 2

В качестве альтернативы (для ответа icza) вы можете использовать пакет WaitGroup из sync и анонимную функцию, чтобы избежать изменения оригинала SayHello.

package main

import (
    "fmt"
    "sync"
)

func SayHello() {
    for i := 0; i < 10; i++ {
        fmt.Print(i, " ")
    }
}

func main() {
    SayHello()

    var wg sync.WaitGroup
    wg.Add(1)

    go func() {
        defer wg.Done()
        SayHello()
    }()

    wg.Wait()
}

Чтобы печатать номера одновременно, запускайте каждый оператор печати в отдельной процедуре, как показано ниже.

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(fnScopeI int) {
            defer wg.Done()

            // next two strings are here just to show routines work simultaneously
            amt := time.Duration(rand.Intn(250))
            time.Sleep(time.Millisecond * amt)

            fmt.Print(fnScopeI, " ")
        }(i)
    }

    wg.Wait()
}

Ответ 3

Как уже упоминалось, программа Go завершается, когда возвращается main функция.

Один из вариантов - использовать что-то вроде sync.WaitGroup чтобы подождать другие программы, которые породил main, прежде чем вернуться из main.

Другой вариант - вызвать runtime.Goexit() в main. От Годока:

Goexit завершает программу, которая вызывает его. Никакой другой горутин не затронут. Goexit запускает все отложенные вызовы до завершения программы. Поскольку Goexit не является паникой, любые вызовы восстановления в этих отложенных функциях будут возвращать ноль.

Вызов Goexit из главной программы завершает эту процедуру без возврата функции func main. Поскольку функция func main не вернулась, программа продолжает выполнение других программ. Если все другие программы завершаются, программа вылетает.

Это позволяет основной процедуре прекращать выполнение, в то время как фоновые процедуры продолжают выполняться. Например:

package main

import (
    "fmt"
    "runtime"
    "time"
)

func f() {
    for i := 0; ; i++ {
        fmt.Println(i)
        time.Sleep(10 * time.Millisecond)
    }
}

func main() {
    go f()
    runtime.Goexit()
}

Это может быть чище, чем блокирование навсегда в основной функции, особенно для бесконечных программ. Недостатком является то, что если все процедуры процесса возвращаются или завершаются (включая основную программу), Go обнаружит это как ошибку и панику:

fatal error: no goroutines (main called runtime.Goexit) - deadlock!

Чтобы избежать этого, по крайней мере одна программа должна вызвать os.Exit прежде чем она вернется. Вызов os.Exit(0) немедленно завершает программу и указывает, что она сделала это без ошибок. Например:

package main

import (
    "fmt"
    "os"
    "runtime"
    "time"
)

func f() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
        time.Sleep(10 * time.Millisecond)
    }
    os.Exit(0)
}

func main() {
    go f()
    runtime.Goexit()
}