本人小白,自学golang,记录日常。
在学习channel后打算自己设计一个生产者消费者模型,以下是记录过程中遇见的一些难点和最后的解决方案:
要求:
汽车厂生产汽车交给店员,有10个经销商从店员这买汽车,店员一次只能持有固定的数量。如果店中有空位再放汽车话则通知生产者继续生产;如果店员没有汽车了,店员让经销商等一下,如果店中有汽车了再通知经销商取一下,缺货一定程度再扩建一个厂继续生产。
在设计这串代码中总结的自己认为比较关键的点:
1. 当生产者(汽车厂)和消费者(经销商)两个goroutine同时共用一个channel同时在读写时,如何保证写入channel的信息让另外一个goroutine读取而不是写入goroutine本身去读取?
我思考后给出的解决方案是用延时功能在写入channel信息后加
time.Sleep(1 * time.Second)
这样让本身goroutine不会读取自己写入的数据
*********************************************************************************
对于上面这个问题是之前的想法,其实想错了,解决这个问题的本身原因是判断通道是否为空,我加延时功能只是碰巧解决了问题而并没有理解透彻,应该为当缓存充足且生产速率>=消费速率的时候,通道不为空,才能解决以上问题 。
特意回来再记录一下。
*************************************************************************************
2.语法问题
if语句后只能写bool类型的channel i/o 判断语句例如这样
if <-bools
而不能写
if <- channel //该channel类型为int
遇见这种情况应该用select语句去设计代码,写i/o语句
3.依旧是语法问题 ,break只能跳出case中的循环,如果想跳出协程的循环应该使用return
下面是最后写出来的代码仅供参考
package main
import (
"fmt"
"math/rand"
"strconv"
"time"
)
func producter(product chan int, bools chan bool) {
yield := 0
for {
select {
case count := <-product:
if count > 0 {
count = count + 5
fmt.Println("莫亚工厂开始生产汽车,目前老厂汽车存量", count)
} else if count == 0 {
count = count + 10
fmt.Printf("莫亚工厂全员开始生产汽车,目前老厂汽车存量%d\n", count)
yield = yield + 1
fmt.Println("yield=", yield)
if yield == 20 {
fmt.Println("吕志文超级工厂产量不足,工厂开始考虑扩厂,新厂房建设中....")
bools <- true
}
}
product <- count
time.Sleep(1 * time.Second)
default:
product <- 1
fmt.Println("莫亚工厂即将开始生产")
}
}
}
func consumer(product chan int, name string, size int, newproduct chan int) {
allcount := 0
for {
select {
case newcount := <-newproduct:
count := <-product
allcount = count + newcount
if allcount > size {
allcount = allcount - size
fmt.Printf("经销商"+name+"购买了%d辆汽车,当前存量%d\n", size, allcount)
x := rand.Intn(size)
y := size - x
product <- count - x
newproduct <- newcount - y
time.Sleep(10 * time.Second)
} else if count <= size {
size = count
fmt.Printf("经销商"+name+"购买了%d辆汽车,开始缺货\n", size)
product <- 0
newproduct <- 0
time.Sleep(10 * time.Second)
}
default:
count := <-product
if count > size {
count = count - size
fmt.Printf("经销商"+name+"购买了%d辆汽车,当前存量%d\n", size, count)
} else if count <= size {
size = count
fmt.Printf("经销商"+name+"购买了%d辆汽车,开始缺货\n", size)
count = 0
}
product <- count
time.Sleep(10 * time.Second)
}
}
}
func newproducter(newproduct chan int, newbools chan bool) {
newyield := 0
for {
select {
case newcount := <-newproduct:
if newcount > 0 {
newcount = newcount + 5
fmt.Println("莫亚新工厂开始生产汽车,目前新厂汽车存量", newcount)
newproduct <- newcount
time.Sleep(1 * time.Second)
} else if newcount == 0 {
newcount = newcount + 5
fmt.Println("莫亚新工厂开始生产汽车,目前新厂汽车存量", newcount)
newproduct <- newcount
time.Sleep(1 * time.Second)
newyield++
if newyield == 2 {
fmt.Println("莫亚两个厂产量依旧足,考虑再扩厂或者研发新生产技术....")
newbools <- true
}
}
default:
newproduct <- 1
fmt.Println("新厂建设完毕,正式投入生产")
//time.Sleep(1 * time.Second)
}
}
}
func main() {
product := make(chan int, 20)
bools := make(chan bool)
newproduct := make(chan int, 20)
newbools := make(chan bool)
go producter(product, bools)
time.Sleep(10 * time.Second) //先让工厂生产10s
for i := 1; i < 10; i++ {
go consumer(product, strconv.Itoa(i), rand.Intn(10)+5, newproduct)
//每个客户分批次拿货
time.Sleep(1 * time.Second)
}
for {
if <-bools {
break
}
}
go newproducter(newproduct, newbools)
for {
if <-newbools {
break
}
}
}