从通道中添加切片

huangapple go评论74阅读模式
英文:

Slice append from channels

问题

我想创建一个切片,并将从通道返回的值添加到切片中。以下是我尝试过但无法解决的代码。

我必须发送切片的地址,但是我不知道如何做到这一点 从通道中添加切片

package main

import "fmt"
import "time"

func sendvalues(cs chan int){
    for i:=0;i<10;i++{
        cs<-i    
    }    
}

func appendInt(cs chan int, aINt []int)[]*int{
    for {
        select {
        case i := <-cs:
            aINt = append(aINt,i)//append返回新类型对吗?
            fmt.Println("slice",aINt)
        }   
    }   
}



func main() {
    cs := make(chan int)
    intSlice := make([]int, 0,10)

    fmt.Println("Before",intSlice)
    go sendvalues(cs)
    go appendInt(cs,intSlice)//我必须在这里传递地址

    time.Sleep(999*999999)
    fmt.Println("After",intSlice)
}
英文:

I want to create slice and add values returned from channel.
Below is the code I tried but could not able to solve it.

I have to send address of the slice, but I am not able to figure out how 从通道中添加切片

package main

import &quot;fmt&quot;
import &quot;time&quot;

func sendvalues(cs chan int){
	for i:=0;i&lt;10;i++{
		cs&lt;-i	
	}	
}

func appendInt(cs chan int, aINt []int)[]*int{
	for {
		select {
		case i := &lt;-cs:
			aINt = append(aINt,i)//append returns new type right ?
			fmt.Println(&quot;slice&quot;,aINt)
		}   
    }   
}



func main() {
	cs := make(chan int)
	intSlice := make([]int, 0,10)

	fmt.Println(&quot;Before&quot;,intSlice)
	go sendvalues(cs)
	go appendInt(cs,intSlice)// I have to pass address here

	time.Sleep(999*999999)
	fmt.Println(&quot;After&quot;,intSlice)
}

答案1

得分: 14

你的代码有两个(实际上是三个)原因导致无法正常工作:

  1. append 在容量达到上限时会返回一个新的切片。因此,在 appendInt 中的赋值语句将不起作用。

  2. appendInt 在并发运行,因此:

    • 只要 appendInt 没有向 main 发送完成的消息,main 就不知道 intSlice 是否已经包含了你想要的所有值。
    • 你必须在 main 结束时等待所有的 goroutine 返回。

问题1:在函数中修改切片

你可能知道,在 Go 中,你传递给函数的每个值都会被复制。引用类型,比如切片,也会被复制,但是在内部会有指针指向原始的内存位置。这意味着你可以在函数中修改切片的元素。但是你不能将这个值重新赋值为一个新的切片,因为内部的指针将指向不同的位置。你需要使用指针来实现这个。以下是一个示例(Play):

func modify(s *[]int) {
    for i := 0; i < 10; i++ {
        *s = append(*s, i)
    }
}

func main() {
    s := []int{1, 2, 3}
    modify(&s)
    fmt.Println(s)
}

问题2:同步 goroutine

要等待已启动的 goroutine,你可以使用 sync.WaitGroup。以下是一个示例(Play):

func modify(wg *sync.WaitGroup, s *[]int) {
    defer wg.Done()
    for i := 0; i < 10; i++ {
        *s = append(*s, i)
    }
}

func main() {
    wg := &sync.WaitGroup{}
    s := []int{1, 2, 3}

    wg.Add(1)
    go modify(wg, &s)

    wg.Wait()
    fmt.Println(s)
}

上面的示例使用 wg.Wait() 等待 modify 完成(modify 在完成时调用 wg.Done())。如果移除 wg.Wait() 的调用,你将看到为什么不同步是一个问题。输出的比较如下:

  • 使用 wg.Wait()[1 2 3 0 1 2 3 4 5 6 7 8 9]
  • 不使用 wg.Wait()[1 2 3]

主 goroutine 会比 modify goroutine 先返回,这就是为什么你永远看不到修改后的结果。因此,同步是绝对必要的

一个好的方法是使用通道来传递新的切片。你不需要使用指针,并且可以实现同步。以下是一个示例(Play):

func modify(res chan []int) {
    s := []int{}
    for i := 0; i < 10; i++ {
        s = append(s, i)
    }
    res <- s
}

func main() {
    c := make(chan []int)
    go modify(c)

    s := <-c
    fmt.Println(s)
}
英文:

Your code won't work for two (in fact three) reasons:

  1. append returns a new slice as soon as the capacity is reached.
    Thus, the assignment in appendInt will do nothing.

  2. appendInt runs concurrently, therefore:

    • As long as appendInt does not message main that it is finished,
      main does not know when the intSlice has all the values you want.
    • You have to wait for all goroutines to return at the end of main

Problem 1: Modifying slices in functions

You may know that in Go every value you pass to a function is copied. Reference values,
such as slices, are copied too, but have pointers internally which then point to the original memory location. That means you can modify the elements of a slice in a function. What you
can't do is reassigning this value with a new slice as the internal pointer would point to somewhere different. You need pointers for that. Example (Play):

func modify(s *[]int) {
	for i:=0; i &lt; 10; i++ {
		*s = append(*s, i)
	}
}

func main() {
	s := []int{1,2,3}
	modify(&amp;s)
	fmt.Println(s)
}

Problem 2: Synchronizing goroutines

To wait for started goroutines, you can use a sync.WaitGroup. Example (Play):

func modify(wg *sync.WaitGroup, s *[]int) {
	defer wg.Done()
	for i:=0; i &lt; 10; i++ {
		*s = append(*s, i)
	}
}

func main() {
	wg := &amp;sync.WaitGroup{}
	s := []int{1,2,3}
	
	wg.Add(1)
	go modify(wg, &amp;s)
	
	wg.Wait()
	fmt.Println(s)
}

The example above waits (using wg.Wait()) for modify to finish
(modify calls wg.Done() when finished). If you remove the wg.Wait() call, you will
see why not synchronizing is a problem. Comparison of outputs:

  • With wg.Wait(): [1 2 3 0 1 2 3 4 5 6 7 8 9]
  • Without wg.Wait(): [1 2 3]

The main goroutine returns earlier than the modify goroutine which is why you will never
see the modified results. Therefore synchronizing is absolutely necessary.

A good way to communicate the new slice would be to use a channel. You would not need to
use pointers and you would have synchronization. Example (Play):

func modify(res chan []int) {
	s := []int{}
	for i:=0; i &lt; 10; i++ {
		s = append(s, i)
	}
	res &lt;- s
}

func main() {
	c := make(chan []int)
	go modify(c)
	
	s := &lt;-c
	fmt.Println(s)
}

huangapple
  • 本文由 发表于 2013年11月7日 01:36:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/19818878.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定