莫亚笔记 golang 生产者消费者模型扩展(二)

         上一篇中写的代码写了后我在想如何将方法,封装函数,结构体等知识点融合到生产者消费者模型中,经过几天不断的尝试,重新构建了生产者消费者模型代码,解决了实际生产消费中生产动态平衡,以及线程安全,检测负载情况等问题。下面是我在写代码的一些总结:

        1. 如何解决线程安全问题

        要想解决这个问题首先得明白什么是线程安全,用我自己的理解是从实际微观角度看计算机,一个时间点cpu只能执行一段指令,而当有多个线程同时在并发时,会出现资源数据抢夺的情况,比如下面这段代码

package main

import (
	"fmt"
	"time"
)

func main() {
	i := 0

	for j := 0; j < 10000; j++ {
		go consumer(&i)
	}

	time.Sleep(2 * time.Second)
	fmt.Println(i)
}

func consumer(i *int) {
	*i++
}

理论上运行结果应该是10000,但是结果却每次都不相同,原因就是协程运算i++时,读取的i的数据这一指令时因为计算机正在执行其他指令,导致本来该读取i=8000,再进行i++运算,变成了读取之前存入内存的i的数据7999,再i++最后得到的值就从本来的8001变成了8000,这就是因为线程并发导致的数据不安全的问题。

       在生产者消费者模型中也会遇见同样得问题,我选择将生产消费读写数据的chan缓存区设定为1来解决线程安全的问题,简单来说:当缓冲区大于1,就不能确保只有一个消费者在执行,会出现资源抢夺导致消费者读写chan的数据会出现错误,比如有两个消费者在运行,这时可能出现当消费者1在读完数据准备写入数据的时候,时间片走完了,这个时候系统执行消费者2的读取数据操作,本来消费者2读取的数据应该是消费者1写入通道给生产者再由生产者将读取的数据运算后再写入通道的数据,而由于资源抢夺,消费者2读取不了消费者1的写入的数据,因为消费者1还没有来得及写入,这时消费者2读取的数据则是之前消费者1读取的数据,导致数据发生错误,出现数据线程不安全的情况,而当我将缓冲区设置为1时,生产者生产了通道满了不会继续生产,消费者读取了通道空了,通道也阻塞了,能确保只有一个消费者在和生产者读写数据,从而解决线程安全问题。

**********************************************************************************************************

        想了半天的线程安全问题,刚刚才知道原来channel本身就是线程安全的,因为channel收发数据都是原子性的,go设计思想本身就是不用共享数据来通信,而是用通信来共享数据,而后者就是channel,channel设计的目的就是多任务间传递数据,所以已经是安全的了,和缓存区无关了,哈哈哈有点尴尬,上面我还想了那么多设计个缓存区为1,现在算是明白了go的线程安全问题了,特意过来纠错~ 这是尴尬的分割线

***********************************************************************************************************

     2. 如何产能动态平衡问题

     将多个工厂放入一个切片里,然后迭代切片每个元素,选择货存高的元素(工厂)来卖汽车给消费者,这就是我的一个解决思路。

    3.值传递的总结

     由于函数的实参不能改变形参,所以当在建立生产者消费者结构体的时候,需要引用指针作为参数来解决值传递的问题。

    4.语法总结

     break,continue中断或者跳过的时最近的循环

***********************************************************************************************

下面贴上我的新生产消费者模型代码

***********************************************************************************************

package main

import (
	"fmt"
	"math/rand"
	"strconv"
	"time"
)

type Consumer struct {
	name         string
	quantity     int //需求量
	realquantity int //实际需求量
}

type Factory struct {
	name        string
	capacity    int //产能
	productchan chan int
	done        bool //负载情况
	stock       int  //标定库存
	nowstock    int  //实际库存
}

func (oneFactory *Factory) produce() {
	yield := 0
	for {
		select {
		case count := <-oneFactory.productchan:
			if 0 < count && count < oneFactory.stock {
				count = count + oneFactory.capacity
				fmt.Printf("%v正在努力生产中...当前库存为%v\n", oneFactory.name, count)
			} else if count >= oneFactory.stock {
				fmt.Printf("%v当前库存量达到或超过标定库存,暂停生产,当前库存为%v\n", oneFactory.name, count)
			} else if count == 0 {
				yield++
				count = 2 * oneFactory.capacity
				fmt.Printf("%v产能翻倍...当前库存为%v\n", oneFactory.name, count)
				if yield == 5 {
					fmt.Println(oneFactory.name + "产量不足,工厂开始扩厂,新厂房建设中....")
					oneFactory.done = true
				}

			}
			oneFactory.productchan <- count
			oneFactory.nowstock = count
			time.Sleep(1 * time.Second)
		default:
			oneFactory.productchan <- oneFactory.nowstock

		}
	}

}
func (aconsumer *Consumer) consumer(addrslice *[]*Factory) {
	for {
		f := *addrslice
		//循环
		//工厂总线以串行顺序满足消费者
	in:
		for i, factory := range f {
			//当前工厂实际库存小于下一个工厂,跳过此工厂
			//找出实际库存量最大的工厂,维持工厂实际库存动态平衡
			if i < len(f)-1 && (*factory).nowstock < (*f[i+1]).nowstock {
				continue
			}
			//初始化真实需求量
			//若实际需求量为0时,重新满上
			if aconsumer.realquantity == 0 {
				aconsumer.realquantity = aconsumer.quantity
			}
			if count := <-(*factory).productchan; count > 0 {
				if count > aconsumer.quantity {
					count = count - aconsumer.realquantity
					fmt.Printf(aconsumer.name+"在"+(*factory).name+"购买了%d辆汽车,当前汽车存量%v\n", aconsumer.realquantity, count)
					(*factory).productchan <- count
					(*factory).nowstock = count
					time.Sleep(5 * time.Second)
					//此工厂能满足产量,跳过下一个工厂
					break in
				} else {
					fmt.Printf(aconsumer.name+"在"+(*factory).name+"购买了%d辆汽车,"+(*factory).name+"已经开始缺货\n", count)
					aconsumer.realquantity = aconsumer.realquantity - count
					//当前工厂实际库存量不能完全满足客户需求量
					//将客户实际需求量到下一个工厂进行供应
					//当前工厂实际库存已经被清空
					(*factory).productchan <- 0
					(*factory).nowstock = 0
				}

			}

		}

	}
}

func main() {
	//工厂一 储备能力 200 ,生产能力 10 chan缓存为 1
	//保证消费到工厂产品时,在同一时刻,只有一个消费者在消费
	//消费者读取chan时候,当其中一个消费者拿到数据时候,其他消费者会强制排队
	//保证数据线程安全
	oneFactory := Factory{
		name:        "0号工厂",
		capacity:    10,
		productchan: make(chan int, 1),
		done:        false,
		stock:       200,
	}
	go oneFactory.produce()
	//创建工厂总线
	var slice []*Factory
	slice = append(slice, &oneFactory)
	go func() {
		for i := 0; i < 30; i++ {
			aconsumer := Consumer{
				name:     "经销商" + strconv.Itoa(i),
				quantity: rand.Intn(10) + 2,
			}
			go aconsumer.consumer(&slice)
			//等待是为了前期chan压力减小
			//后面工厂新增之后分担到另外到chan上面
			//也为了控制台打印平滑一点
			time.Sleep(1 * time.Second)

		}
	}()
	for {
		lastFactory := slice[len(slice)-1]
		for lastFactory.done {
			twoFactory := Factory{
				name:        strconv.Itoa(len(slice)) + "号工厂",
				productchan: make(chan int, 1),
				stock:       200,
				done:        false,
				capacity:    10,
			}
			go twoFactory.produce()
			fmt.Println(twoFactory.name + "马上上线")
			slice = append(slice, &twoFactory)
			//跳出内层循环
			//一直监视工厂总线最后一个工厂负载状态
			break

		}
	}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值