使用通道退出永久循环-Go游乐场问题
我试图实现一个简单的逻辑,其中生产者使用永久使用通道退出永久循环-Go游乐场问题,go,go-playground,Go,Go Playground,我试图实现一个简单的逻辑,其中生产者使用永久for循环将数据发送到通道ch,消费者从通道ch读取数据 当生产者在通道quit上接收到信号时,生产者停止生产并退出永久循环 代码如下(另请参见) func main(){ ch:=制造(成交量) 退出:=make(chan bool) var wg sync.WaitGroup 工作组.添加(1) 开始生产(ch、退出和工作组) 去消费(ch) 时间。睡眠(1*时间。毫秒) fmt.Println(“关闭”) 关闭(退出) wg.Wait() } f
for
循环将数据发送到通道ch
,消费者从通道ch
读取数据
当生产者在通道quit
上接收到信号时,生产者停止生产并退出永久循环
代码如下(另请参见)
func main(){
ch:=制造(成交量)
退出:=make(chan bool)
var wg sync.WaitGroup
工作组.添加(1)
开始生产(ch、退出和工作组)
去消费(ch)
时间。睡眠(1*时间。毫秒)
fmt.Println(“关闭”)
关闭(退出)
wg.Wait()
}
func product(ch chan int,quit chan bool,wg*sync.WaitGroup){
对于i:=0;;i++{
挑选{
case您创建了一个不应该在真实程序中发生的病态情况,因此调度程序没有优化以处理此问题。再加上操场上的假时间实现,在达到超时之前,您会得到太多的生产者和消费者周期
制作人goroutine正在尽可能快地创建值,而消费者总是准备好接收这些值。使用GOMAPXPROCS=1
,调度程序将所有时间都花在这两者之间,然后被迫抢占可用的工作来检查主goroutine,这比游乐场允许的时间要长
如果我们为生产者-消费者对添加一些要做的事情,我们可以限制他们垄断调度程序的时间。例如,向消费者添加time.Sleep(time.Microsecond)
,将导致游乐场打印1000个值。这也表明了“精确性”模拟时间在操场上,因为正常的硬件不可能做到这一点,因为处理每条消息需要非零的时间
虽然这是一个有趣的案例,但它与实际的程序几乎没有关系
请注意,您可以通过通道范围
来接收所有值,您应该始终延迟wg.Done
在goroutine开始时,如果可能,您可以在选择案例中发送值,该案例允许您在发送未就绪时实际取消for-select循环,并且如果您希望“退出消费者”消息您还需要将WaitGroup
发送给消费者
func main(){
ch:=制造(成交量)
退出:=make(chan bool)
var wg sync.WaitGroup
工作组.添加(2)
开始生产(ch、退出和工作组)
去消费(ch和wg)
时间。睡眠(50*时间。微秒)
fmt.Println(“关闭”)
关闭(退出)
wg.Wait()
}
func product(ch chan int,quit chan bool,wg*sync.WaitGroup){
推迟工作组完成()
对于i:=0;;i++{
挑选{
如果您的goroutine基本上不起作用,那么就创建了一个“繁忙循环”"在生产者和消费者之间。如果运行时抢占这一点需要一定的时间,而且在操场上时间太长。操场上的假时间实现也可能存在问题,在繁忙循环中的goroutines理论上可以打印出比停止前预期多得多的值,从而导致大量的pr处理处理输出缓冲区所花费的时间。仔细观察,虽然过于繁忙的生产者和消费者很容易解释,但我认为操场时间是这里更有趣的事情。就像在科学中我们使用理想气体或球形奶牛一样,操场上的goroutines似乎以“无限速度”运行在可观察的范围之外。但即使在您发布的链接中,消费者打印也会出现在生产者打印之前。这是故意的吗?@Inian:print语句发生在发送和接收完成之后,否则不会同步。您看到每个值的顺序并不重要。
func main() {
ch := make(chan int)
quit := make(chan bool)
var wg sync.WaitGroup
wg.Add(1)
go produce(ch, quit, &wg)
go consume(ch)
time.Sleep(1 * time.Millisecond)
fmt.Println("CLOSE")
close(quit)
wg.Wait()
}
func produce(ch chan int, quit chan bool, wg *sync.WaitGroup) {
for i := 0; ; i++ {
select {
case <-quit:
close(ch)
fmt.Println("exit")
wg.Done()
return //we exit
default:
ch <- i
fmt.Println("Producer sends", i)
}
}
}
func consume(ch chan int) {
for {
runtime.Gosched() // give the opportunity to the main goroutine to close the "quit" channel
select {
case i, more := <-ch:
if !more {
fmt.Println("exit consumer")
return
}
fmt.Println("Consumer receives", i)
}
}
}
func main() {
ch := make(chan int)
quit := make(chan bool)
var wg sync.WaitGroup
wg.Add(2)
go produce(ch, quit, &wg)
go consume(ch, &wg)
time.Sleep(50 * time.Microsecond)
fmt.Println("CLOSE")
close(quit)
wg.Wait()
}
func produce(ch chan int, quit chan bool, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; ; i++ {
select {
case <-quit:
close(ch)
fmt.Println("exit")
return
case ch <- i:
fmt.Println("Producer sends", i)
}
}
}
func consume(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for i := range ch {
fmt.Println("Consumer receives", i)
time.Sleep(time.Microsecond)
}
fmt.Println("exit consumer")
return
}