一尘不染

Goroutine是协作调度的。这是否意味着不执行的goroutine将导致goroutine逐一运行?

go

来自:http :
//blog.nindalf.com/how-goroutines-work/

由于goroutine是协同调度的,因此不断循环的goroutine会使同一线程上的其他goroutine饿死。

Goroutine很便宜,并且如果被阻塞,不会导致对其进行复用的线程阻塞

  • 网络输入
  • 睡眠
  • 渠道运营或
  • 阻塞sync包中的原语。

因此,鉴于以上所述,假设您有一些类似这样的代码,除了循环随机次数并打印总和外,什么也不做:

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将一一运行吗?


阅读 202

收藏
2020-07-02

共1个答案

一尘不染

克里克所有评论的汇编和整理。

抢占式意味着内核(运行时)允许线程运行特定时间,然后在其他线程不做任何事情或一无所知的情况下将其执行。在OS内核中,通常使用硬件中断来实现。进程不能阻止整个操作系统。在协作式多任务处理中,线程必须显式地让他人执行。如果不是这样,它可能会阻塞整个过程甚至整个机器。Go就是这样做的。它在goroutine可以产生执行的一些非常特定的点。但是,如果goroutine仅针对{}执行,那么它将锁定整个过程。

但是,引用中没有提及运行时中的最新更改。fmt.Println(sum)可能导致其他goroutine被调度,因为较新的运行时将在函数调用时调用调度程序。

如果您没有任何函数调用,只需进行一些数学运算,那么是的,goroutine将锁定线程,直到其退出或命中可能使他人执行的东西。这就是为什么for {}在Go中不起作用。更糟糕的是,由于GO的工作原理,即使GOMAXPROCS>
1,它仍然会导致进程挂起,但是无论如何您都不应依赖于此。很高兴了解这些东西,但不要指望它。甚至有建议在像您这样的循环中插入调度程序调用

Go的运行时所做的主要事情是尽其所能,使每个人都可以执行而不会饿死任何人。语言规范中未对此进行规定,并且将来可能会更改。如果关于循环的提议将被实施,那么即使没有函数调用也可能发生切换。目前,您唯一需要记住的是,在某些情况下,函数调用可能导致goroutine产生执行。

为了解释Akavall的答案中的切换,当fmt.Printf被调用时,它要做的第一件事是检查是否需要增加堆栈并调用调度程序。它可能会切换到另一个goroutine。是否切换取决于其他goroutine的状态以及调度程序的确切实现。像任何调度程序一样,它可能会检查是否有饥饿的goroutines应该执行。在许多迭代中,函数调用有更大的机会进行切换,因为其他人的饥饿时间更长。经过几次迭代,goroutine在饥饿发生之前就完成了。

2020-07-02