Пока SayHello()
выполняется так, как ожидалось, goroutine ничего не печатает.
package main
import "fmt"
func SayHello() {
for i := 0; i < 10 ; i++ {
fmt.Print(i, " ")
}
}
func main() {
SayHello()
go SayHello()
}
Пока SayHello()
выполняется так, как ожидалось, goroutine ничего не печатает.
package main
import "fmt"
func SayHello() {
for i := 0; i < 10 ; i++ {
fmt.Print(i, " ")
}
}
func main() {
SayHello()
go SayHello()
}
Когда ваша функция 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
В качестве альтернативы (для ответа 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()
}
Как уже упоминалось, программа 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()
}