来自:http : //blog.nindalf.com/how-goroutines-work/
由于goroutine是协同调度的,因此不断循环的goroutine会使同一线程上的其他goroutine饿死。 Goroutine很便宜,并且如果被阻塞,不会导致对其进行复用的线程阻塞 网络输入 睡眠 渠道运营或 阻塞sync包中的原语。
由于goroutine是协同调度的,因此不断循环的goroutine会使同一线程上的其他goroutine饿死。
Goroutine很便宜,并且如果被阻塞,不会导致对其进行复用的线程阻塞
因此,鉴于以上所述,假设您有一些类似这样的代码,除了循环随机次数并打印总和外,什么也不做:
func sum(x int) { sum := 0 for i := 0; i < x; i++ { sum += i } fmt.Println(sum) }
如果您使用goroutines之类的
go sum(100) go sum(200) go sum(300) go sum(400)
如果只有一个线程,goroutine将一一运行吗?
抢占式意味着内核(运行时)允许线程运行特定时间,然后在其他线程不做任何事情或一无所知的情况下将其执行。在OS内核中,通常使用硬件中断来实现。进程不能阻止整个操作系统。在协作式多任务处理中,线程必须显式地让他人执行。如果不是这样,它可能会阻塞整个过程甚至整个机器。Go就是这样做的。它在goroutine可以产生执行的一些非常特定的点。但是,如果goroutine仅针对{}执行,那么它将锁定整个过程。
但是,引用中没有提及运行时中的最新更改。fmt.Println(sum)可能导致其他goroutine被调度,因为较新的运行时将在函数调用时调用调度程序。
fmt.Println(sum)
如果您没有任何函数调用,只需进行一些数学运算,那么是的,goroutine将锁定线程,直到其退出或命中可能使他人执行的东西。这就是为什么for {}在Go中不起作用。更糟糕的是,由于GO的工作原理,即使GOMAXPROCS> 1,它仍然会导致进程挂起,但是无论如何您都不应依赖于此。很高兴了解这些东西,但不要指望它。甚至有建议在像您这样的循环中插入调度程序调用
for {}
Go的运行时所做的主要事情是尽其所能,使每个人都可以执行而不会饿死任何人。语言规范中未对此进行规定,并且将来可能会更改。如果关于循环的提议将被实施,那么即使没有函数调用也可能发生切换。目前,您唯一需要记住的是,在某些情况下,函数调用可能导致goroutine产生执行。
为了解释Akavall的答案中的切换,当fmt.Printf被调用时,它要做的第一件事是检查是否需要增加堆栈并调用调度程序。它可能会切换到另一个goroutine。是否切换取决于其他goroutine的状态以及调度程序的确切实现。像任何调度程序一样,它可能会检查是否有饥饿的goroutines应该执行。在许多迭代中,函数调用有更大的机会进行切换,因为其他人的饥饿时间更长。经过几次迭代,goroutine在饥饿发生之前就完成了。
fmt.Printf