并发版schedule的select是不是会导致crash?

来源:16-4 队列实现调度器

慕哥4405024

2018-11-08

老师,看了并发版的schedule的代码有一个疑问,代码如下:

func (s *QueuedScheduler) Run() {
	s.workerChan = make(chan chan engine.Request)
	s.requestChan = make(chan engine.Request)
	go func() {
		var requestQ []engine.Request
		var workerQ []chan engine.Request
		for {
			var activeRequest engine.Request
			var activeWorker chan engine.Request
			if len(requestQ) > 0 &&
				len(workerQ) > 0 {
				activeWorker = workerQ[0]
				activeRequest = requestQ[0]
			}

			select {
			case r := <-s.requestChan:
				requestQ = append(requestQ, r)
			case w := <-s.workerChan:
				workerQ = append(workerQ, w)
			case activeWorker <- activeRequest:
			//这个case中,如果schedule工作了一段时间后,某一时刻requestQ先变为0, 
			activeRequest保留的应该是最后一个request,
			这个request对应一个worker channel被保留在activeWorker中,
			当activeWorker 完成的时候,select会从这个case返回吧,
			activeWorker 得到的还是最后的activeRequest。但是我们看下面的代码,
			requestQ = requestQ[1:]这个不是要crash?
				
				workerQ = workerQ[1:]
				requestQ = requestQ[1:]
			}
		}
	}()
}
写回答

1回答

ccmouse

2018-11-11

不会crash的。

我们的并发是多个goroutine会并发。在单个goroutine内,一定是顺序执行的。我们的requestQ是一个局部变量,这个局部变量只会被这一个goroutine操作,没有人会并发的改变它。


我们看到进入case activeWorker <- activeRequest:的前提是len(requestQ) > 0,所以后面一定可以requestQ = requestQ[1:]。


那么不同的goroutine之间怎么同步的呢?通过channel,这就是为什么我们有了requestChan,还要用一个局部变量requestQ来排队。


这个例子正是说明了go语言的csp模型通过(使用channel)通信来共享数据,而不是传统意义上的使用共享数据(比如Queue+Lock)来通信。

0
2
ccmouse
for里面不会留着上一次的值的。每次申明一个activeWorker,他的值就是nil,然后从nil里获取数据的话,这个分支是不会运行到的。变量的生命周期只是在花括号内
2018-11-13
共2条回复

Google资深工程师深度讲解Go语言 由浅入深掌握Go语言

语法+分布式爬虫实战 为转型工程师量身打造

5995 学习 · 1909 问题

查看课程