2013-12-14 2 views
3

Рассмотрим эту программу:Почему эта программа заканчивается в моей системе, но не на детской площадке?

package main 

import "fmt" 
import "time" 
import "runtime" 

func main() { 

    x := 0 

    go func() { 
     time.Sleep(500 * time.Millisecond) 
     x = 1 
    }() 

    for x == 0 { 
     runtime.Gosched() 
    } 

    fmt.Println("it works!") 
} 

Почему это прекратить на месте, но не на Playground? Оканчивает ли моя программа зависимость от неопределенного поведения?

ответ

2

Этот код не предоставляет много гарантий. Он почти полностью опирается на детали реализации вокруг неопределенного поведения.

В большинстве многопоточных систем нет гарантии, что изменение одной нити без барьера будет видно в другом. У вас есть goroutine, который может работать на другом процессоре, вообще говоря, значение переменной, которую никто никогда не сможет прочитать.

for x == 0 { может быть легко переписан на for {, так как никогда не было никаких гарантий, что изменения этой переменной могут быть видны.

Идентификатор гонки также, вероятно, сообщит об этой проблеме. Вы действительно не должны ожидать, что это сработает. Если вы хотите sync.WaitGroup, вы должны просто использовать его так, как он правильно координирует потоки.

+0

Это может быть верно в C, но в Go есть вещи, которые могут быть изменены значение x; одно из них - назначение в процедуре Go.Неверный компилятор, который оптимизирует проверку. – fuz

+0

Видимо, занятое ожидание действительно [неопределенное поведение] (http://golang.org/ref/mem) в Go. Тем не менее, мой предыдущий комментарий. – fuz

+0

@FUZxxl: это не случай оптимизации проверки. См. Мой ответ, почему код гарантированно не работает на детской площадке. –

5

На игровой площадке Go используется специальная реализация time.Sleep, предназначенная для предотвращения монополизации отдельных программ на ресурсы задней части сайта.

Как описано в this article about the how the playground is implemented, goroutines, вызывающие time.Sleep(), усыпляются. Передняя часть детской площадки ждет, пока все остальные горуты не будут заблокированы (что в противном случае было бы тупиком), а затем разбудит горутин с самым коротким временем.

В вашей программе у вас есть два гортани: главный, и один, который вызывает time.Sleep. Поскольку главный горутин никогда не блокируется, вызов time.Sleep никогда не вернется. Программа продолжается до тех пор, пока она не превысит выделенное ей время ЦП и затем прекратится.

+0

+1 для приятного объяснения причин неудачной работы программы на игровой площадке. Я отправил ответ, который пытается объяснить, почему программа не гарантирует правильную работу в общем случае (в соответствии с правилами модели памяти Go. – ChrisH

4

Модель памяти Go не гарантирует, что значение, записанное в x в goroutine, будет наблюдаться основной программой. Аналогично ошибочная программа приведена в качестве примера в разделе, посвященном go routine destruction. Модель памяти Go также специально вызывает ожидание ожидания без синхронизации в качестве неправильной идиомы в this section.

Вам необходимо выполнить какую-то синхронизацию в goroutine, чтобы гарантировать, что x = 1 происходит до одной из итераций цикла for в main.

Вот версия программы, которая гарантированно будет работать по назначению.

http://play.golang.org/p/s3t5_-Q73W

package main 

import (
    "fmt" 
    "time" 
) 

func main() { 
    c := make(chan bool) 
    x := 0 

    go func() { 
     time.Sleep(500 * time.Millisecond) 
     x = 1 
     close(c) // 1 
    }() 

    for x == 0 { 
     <-c // 2 
    } 

    fmt.Println("it works!") 
} 

Гарантии Go Модель памяти, что линия, помеченные // 1 происходит перед линией, обозначенной // 2. В результате, для цикла гарантированно завершить до его вторая итерация.

Смежные вопросы