Самая очевидная ошибка в вашем коде состоит в том, что Inner.Run()
имеет значение-приемник, что означает, что он получает копию типа Inner
. Когда вы изменяете это, вы изменяете копию, и вызывающий объект не видит никаких изменений в значении Inner
.
Так первый изменить его, чтобы иметь указатель-приемник:
func (c *Inner) Run(value int) {
// ...
}
Если метод имеет указатель-приемник, адрес (указатель) значения метод вызывается на будет передан методу , И внутри метода вы будете изменять значение , указывающее значение, а не указатель. Указатель указывает на то же значение, которое присутствует у вызывающего, поэтому одно и то же значение изменяется (а не копия).
Только это изменение может заставить ваш код работать. Однако вывод вашей программы не является детерминированным, потому что вы изменяете переменную (поле) из одного goroutine, и вы читаете эту переменную из другого goroutine, поэтому вы должны каким-то образом синхронизировать доступ к этому полю.
Один из способов синхронизации доступа используется sync.RWMutex
:
type Inner struct {
m *sync.RWMutex
Value int
}
При создании значения Outer
, инициализировать этот мьютекс:
o := new(Outer)
o.In.m = &sync.RWMutex{}
Или в одной строке:
o := &Outer{In: Inner{m: &sync.RWMutex{}}}
И в Inner.Run()
блокировка при доступе к е Inner.Value
поле:
func (c *Inner) Run(value int) {
c.m.Lock()
c.Value = value
c.m.Unlock()
for {
c.m.RLock()
fmt.Println(c.Value)
c.m.RUnlock()
time.Sleep(time.Second * 2)
}
}
И вы также должны использовать блокировку при доступе на поле в Outer.Run()
:
func (c Outer) Run() {
go c.In.Run(42)
for {
time.Sleep(time.Second)
c.In.m.RLock()
fmt.Println(c.In)
c.In.m.RUnlock()
}
}
Примечание:
Ваш пример изменяет только Inner.Value
один раз, в начало Inner.Run
. Таким образом, приведенный выше код делает много ненужных блокировок/разблокировок, которые могут быть удалены, если цикл в Outer.Run()
будет ждать до тех пор, пока значение не будет установлено, и после этого оба goroutines могли бы прочитать переменную без блокировки. В общем случае, если переменную можно изменить и в более поздние времена, приведенная выше блокировка/разблокировка требуется при каждом чтении/записи.
Спасибо за вашу решимость! Теперь я понимаю, как это работает. Теперь код работает хорошо. –