@SSSSTTTTAAAASSSS

Работа с goroutines?

Добрый день.
Новичок в GO есть задача с 2-мя горутинами: одна записывает в канал указатель на значения в срезе, вторая их читает.
https://play.golang.org/p/wcNBsu1pjYK
Требуется решение, чтобы программа работала верно и выдавала значения среза.
Насколько понятно из обучающих материалов горутина должна блокироваться после записи в канал до момента его чтения. Птылся в программе сделать небуфферизированный канал, но все также, проскакивают значения.
https://play.golang.org/p/yzRhQOdR8La
На данный момент пока получилось решить, только путем замедления горутины, которая записывает в канал:
https://play.golang.org/p/r4pqhh0tPPW
Но понимаю что решение неправильное.

Возможно кто-то может объяснить какой я момент упускаю.
Заранее благодарен
  • Вопрос задан
  • 147 просмотров
Решения вопроса 1
EvgenyMamonov
@EvgenyMamonov Куратор тега Go
Senior software developer, system architect
Есть несколько вариантов решения задачи
передавать не указатель, а значение

package main

import (
  "fmt"
  "time"
)

var c = make(chan int, 3)
var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

func main() {
  fmt.Println("Hello, playground")
  go save()
  go read()
  time.Sleep(3 * time.Second)
}

func save() {
  for _, val := range data {
    c <- val
  }
}

func read() {
  for {
    val := <-c
    fmt.Println("read:", val)
  }
}

скопировать значение и передать указатель на это значение

package main

import (
	"fmt"
	"time"
)

var c = make(chan *int)
var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

func main() {
	fmt.Println("Hello, playground")
	go save()
	go read()
	time.Sleep(3 * time.Second)
}

func save() {
	for _, val := range data {
	        v := val
		c <- &v
	}
}

func read() {
	for {
		val := <-c
		fmt.Println("read:", *val)
	}
}

передать правильный указатель, на элемент данных

package main

import (
	"fmt"
	"time"


var c = make(chan *int, 5)
var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

func main() {
	fmt.Println("Hello, playground")
	go save()
	go read()
	time.Sleep(3 * time.Second)
}

func save() {
	for i := range data {
		c <- &data[i]
	}
}

func read() {
	for {
		val := <-c
		fmt.Println("read:", *val)
	}
}



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

Вот пример, который покажет, что адрес всегда один и тот же
package main

import (
	"fmt"
	"time"
)

var c = make(chan *int, 5)
var data = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

func main() {
	fmt.Println("Hello, playground")
	go save()
	go read()
	time.Sleep(3 * time.Second)
}

func save() {
	for _, val := range data {
		c <- &val
		fmt.Printf("write: %v\n", &val)
	}
}

func read() {
	for {
		val := <-c
		fmt.Println("read:", *val)
	}
}

Вывод будет таким (адрес один и тот же)
Hello, playground
write: 0xc000094000
write: 0xc000094000
write: 0xc000094000
write: 0xc000094000
write: 0xc000094000
write: 0xc000094000
...


А в такой реализации (2й пример)
func save() {
	for _, val := range data {
		v := val
		c <- &v
		fmt.Printf("write: %v\n", &v)
	}
}

тут вывод будет таким (каждый раз новый участок памяти)
Hello, playground
write: 0xc000094000
write: 0xc000094010
write: 0xc000094018
write: 0xc000094020
write: 0xc000094028
write: 0xc000094030

В третьем варианте тоже будут разные указатели.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы