producer consumer in golang

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

producer consumer in golang

问题

尝试运行以下代码(生产者和消费者)来理解Go语言中的goroutine和channel(从下面的代码片段中删除了包和导入):

var done = make(chan bool)
var msgs = make(chan int)

func produce() {
    for i := 0; i < 10; i++ {
        msgs <- i
    }
    fmt.Println("Before closing channel")
    close(msgs)
    fmt.Println("Before passing true to done")
    done <- true
}

func consume() {
    for {
        msg := <-msgs
        time.Sleep(100 * time.Millisecond)
        fmt.Println("Consumer: ", msg)
    }
}

func main() {
    go produce()
    go consume()
    <-done
    fmt.Println("After calling DONE")
}

根据我对goroutine和channel的理解:
当我们使用go关键字从main()中调用produce()consume()时,Go运行时会启动2个goroutine(类似于Java世界中的线程,但不是实际的操作系统线程),而main()的goroutine会在<-done处停止。
现在,在produce()内部,循环从0到9,循环内部的msgs通道逐个接收整数(0到9),并且这些整数会被consume()并行消费,但是produce()并不知道这一点,它只是不断循环0到9。

问题:假设我上面的理解是正确的。一旦循环结束,为什么produce()内部的下一个Println语句没有被打印出来?为什么msgs通道没有被关闭?为什么goroutine在produce()内部停止,直到消费者消费完所有的消息?

英文:

Tried running the below code(producer & consumer) to understand goroutines and channel in golang (removed package and imports from the code snippet below):

var done = make(chan bool)
var msgs = make(chan int)

func produce() {
    for i := 0; i &lt; 10; i++ {
            msgs &lt;- i
    }
    fmt.Println(&quot;Before closing channel&quot;)
    close(msgs)
    fmt.Println(&quot;Before passing true to done&quot;)
    done &lt;- true
}

func consume() {
    for {
            msg := &lt;-msgs
            time.Sleep(100 * time.Millisecond)
            fmt.Println(&quot;Consumer: &quot;, msg)
    }
}

func main() {
    go produce()
    go consume()
    &lt;-done
    fmt.Println(&quot;After calling DONE&quot;)
}

source code from: http://www.golangpatterns.info/concurrency/producer-consumer

Below is the output when I run the code

Consumer:  0
Consumer:  1
Consumer:  2
Consumer:  3
Consumer:  4
Consumer:  5
Consumer:  6
Consumer:  7
Consumer:  8
Before closing channel
Before passing true to done
After calling DONE

Based on my understanding with goroutines and channel:
When we call produce() and consume() from main() using the go keyword, go runtime kicks of 2 goroutines(sort of threads in java world but not actual OS threads) and the main() goroutine comes and halts at "<-done".
Now inside produce() - the loop goes from 0 to 9 and inside the loop the msgs channel receives the int (0 to 9) 1 at a time which is being consumed in parallel by the consume(); however produce has no clue about it and it just keep looping for 0 to 9.

Question: Assuming my above understanding is correct. Once, the for loop is done why is the next printLine inside produce() not getting printed and also why is the msgs channel not getting closed? Why is the goroutine halting inside the produce() until the consumer consumes all the msgs?

答案1

得分: 9

msgs通道是无缓冲的。这意味着为了完成发送操作,必须有一个相应的接收操作也能完成。这提供了goroutine之间的同步点。

如果你在你的示例中添加一些打印语句,就很容易看出来。

func produce() {
    for i := 0; i < 4; i++ {
        fmt.Println("sending")
        msgs <- i
        fmt.Println("sent")
    }
    fmt.Println("Before closing channel")
    close(msgs)
    fmt.Println("Before passing true to done")
    done <- true
}

func consume() {
    for msg := range msgs {
        fmt.Println("Consumer: ", msg)
        time.Sleep(100 * time.Millisecond)
    }
}

输出:

sending
Consumer:  0
sent
sending
Consumer:  1
sent
sending
Consumer:  2
sent
sending
Consumer:  3
sent
Before closing channel
Before passing true to done
After calling DONE
英文:

The msgs channel is unbuffered. This means that for send to complete, there has to be a corresponding receive operation that can also complete. This provides a synchronization point between goroutines.

It's easy to see if you just add a few more print statements to your example

http://play.golang.org/p/diYQGN-iwE

func produce() {
	for i := 0; i &lt; 4; i++ {
		fmt.Println(&quot;sending&quot;)
		msgs &lt;- i
		fmt.Println(&quot;sent&quot;)
	}
	fmt.Println(&quot;Before closing channel&quot;)
	close(msgs)
	fmt.Println(&quot;Before passing true to done&quot;)
	done &lt;- true
}

func consume() {
	for msg := range msgs {
		fmt.Println(&quot;Consumer: &quot;, msg)
		time.Sleep(100 * time.Millisecond)

	}
}

Output:

sending
Consumer:  0
sent
sending
Consumer:  1
sent
sending
Consumer:  2
sent
sending
Consumer:  3
sent
Before closing channel
Before passing true to done
After calling DONE

答案2

得分: 1

package main

import (
	"fmt"
)

var ch chan int
var ch1 chan struct{}

func main() {
	ch = make(chan int)
	ch1 = make(chan struct{})
	go producer()
	go consumer()
	<-ch1
	close(ch)
	close(ch1)
}

func consumer() {
	for {
		fmt.Println("number", <-ch)
	}
}
func producer() {
	for i := 0; i < 10; i++ {
		ch <- i
	}
	ch1 <- struct{}{}
}

这是一个简单的生产者-消费者模型的示例代码。在main函数中,创建了两个通道chch1,然后启动了生产者和消费者的 goroutine。生产者将数字 0 到 9 发送到通道ch,然后发送一个空结构体到通道ch1。消费者从通道ch接收数据,并打印出来。当从通道ch1接收到数据时,关闭通道chch1,程序结束。

英文:
package main

import (
	&quot;fmt&quot;
)

var ch chan int
var ch1 chan struct{}

func main() {
	ch = make(chan int)
	ch1 = make(chan struct{})
	go producer()
	go consumer()
	&lt;-ch1
	close(ch)
	close(ch1)
}

func consumer() {
	for {
		fmt.Println(&quot;number&quot;, &lt;-ch)
	}
}
func producer() {
	for i := 0; i &lt; 10; i++ {
		ch &lt;- i
	}
	ch1 &lt;- struct{}{}
}

huangapple
  • 本文由 发表于 2015年4月15日 02:18:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/29634649.html
匿名

发表评论

匿名网友

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

确定