golang methods that will yield goroutines

huangapple go评论78阅读模式
英文:

golang methods that will yield goroutines

问题

据我理解,如果goroutine非常繁忙,它们会阻塞其他goroutine的运行。对我来说,这意味着我的应用程序的性能和响应能力可能取决于我了解哪些库方法会将控制权交给其他goroutine(例如通常的Read()和Write())。

有没有办法可以准确地知道不同的库方法如何将控制权交给其他goroutine,而不是实际上阻塞?

有没有办法实现一个调用第三方代码的新方法(包括依赖于waitforsingleobject或waitformultipleobjects的异步Win32 API,比如findnextchangenotification),并且与Go调度器“友好”地交互?在这个特定的例子中,系统调用在完成后会发出信号,我需要等待它完成而不耗尽所有其他的goroutine。

在Go中,处理第三方阻塞操作的另一种“最佳实践”是什么,以防止它们耗尽其他的goroutine?

我猜想,Go运行时可能在后台线程上内部运行某种IO循环,以“暂停”阻塞的goroutine操作,直到它们完成IO。如果确实如此,那么我想能够在新的阻塞操作上构建这一点可能会很有用。

英文:

As far as I can understand, goroutines will block other goroutines from running if they are too busy. For me, this means that the performance and responsiveness of my application will probably depend on me knowing which library methods that will yield control over to other goroutines (e.g. typically Read() and Write())

Is there any way I can know exactly how different library methods will yield control over to other goroutines i.e. not actually block?

Is there any way I can implement a new method that calls third party code (including a async Win32 API like findnextchangenotification that relies on waitforsingleobject or waitformultipleobjects) and behaves "nice" with the Go scheduler? In this particular example, the syscall will signal once finished and I need to wait until it is finished withot exhausting all other goroutines.

Is there perhaps another "best practice" for how to deal with third party blocking operations in Go so that they don't exhaust the other goroutines?

I assume that the Go runtime perhaps have some sort of IO-loop internally running on a background thread in order to "suspend" blocking goroutine operations until they finished with IO. If this is indeed the case, then I guess it could be useful to be able to build upon this for new blocking operations.

答案1

得分: 15

Go的调度器会暂停等待系统调用的goroutine,并在系统调用完成后唤醒它们,为你提供了一种处理异步调用的同步API。

点击这里了解调度器的工作原理。

然而,并没有确切的方法来确定哪个goroutine会在另一个goroutine之后被唤醒,或者直接将控制权从一个goroutine转移到另一个goroutine,这是调度器的工作。

你不必担心这个问题,因为在Go中已经解决了这个问题 - 开始编码吧!

编辑:

进一步澄清一下:你不应该编写代码以符合(或更好地利用)Go调度器的语义 - 相反,应该反过来。也许有一些代码技巧可以稍微提高性能,但调度器可能会在任何未来的Go版本中发生变化 - 使你的代码优化变得无用,甚至对你产生负面影响。

英文:

Go's scheduler will suspend goroutines that are waiting for a syscall and wake them when the syscall has completed, giving you a synchronous API to deal with asynchronous calls.

Read more about how the scheduler works.

There is, however - no exact way of determining which goroutine will be awoken after another or yielding control from one goroutine directly to another - that's the scheduler's job.

Your concern is a solved problem in Go and you don't have to worry about it - code away!

Edit:

To further clarify; you are not supposed to code to conform to (or make better use of) Go's scheduler's semantics -- rather the other way around. There might be some code-tricks that can get you slight performance increase today but the scheduler can and will change in any future Go release -- making your code optimizations useless or even work against you.

答案2

得分: 12

两种机制可以增强对此的控制:

  1. runtime.Gosched() - 将控制权交还给调度器,但当然它对于您已经发出的阻塞调用是无效的。

  2. runtime.LockOSThread() 将一个真实的操作系统线程专用于此 goroutine,没有其他竞争者,这意味着调度器中的竞争会减少。来自文档的说明:

LockOSThread 将调用的 goroutine 绑定到其当前的操作系统线程。直到调用的 goroutine 退出或调用 UnlockOSThread,它将始终在该线程中执行,并且没有其他 goroutine 可以执行。
英文:

Two mechanisms can enhance your control over this:

  1. runtime.Gosched() - yields control back to the scheduler, but of course it won't help with a blocking call you've already issued.

  2. runtime.LockOSThread() dedicates a real OS thread to this goroutine and no other, meaning there will be less competition in the scheduler. from the docs:

LockOSThread wires the calling goroutine to its current operating system thread. Until the calling goroutine exits or calls UnlockOSThread, it will always execute in that thread, and no other goroutine can.

答案3

得分: 5

通过Go 1.1,goroutines只会在阻塞调用(系统调用、通道读写、互斥锁等)时让出控制权。这意味着理论上,goroutines可以完全占用CPU,并且不允许调度器运行。

在Go 1.2中,引入了一个改变来解决这个问题:

在之前的版本中,一个无限循环的goroutine可能会使同一线程上的其他goroutine饿死,当GOMAXPROCS只提供一个用户线程时,这是一个严重的问题。在Go 1.2中,这个问题在一定程度上得到了解决:调度器在进入函数时会偶尔被调用。这意味着任何包含(非内联)函数调用的循环都可以被抢占,从而允许同一线程上的其他goroutine运行。

(来源:go1.2发布说明

理论上,仍然有可能通过不调用非内联函数来占用CPU的goroutines,但是这种情况比在Go 1.2之前永远不进行阻塞调用的goroutines要少得多。

英文:

Through Go 1.1, goroutines would only yield control on blocking calls (syscalls, channel reads/writes, mutex locks, etc). This meant that goroutines could in theory completely hog the CPU, and not allow the scheduler to run.

In Go 1.2, a change was introduced to fix this:

> In prior releases, a goroutine that was looping forever could starve out other goroutines on the same thread, a serious problem when GOMAXPROCS provided only one user thread. In Go 1.2, this is partially addressed: The scheduler is invoked occasionally upon entry to a function. This means that any loop that includes a (non-inlined) function call can be pre-empted, allowing other goroutines to run on the same thread.

(source: go1.2 release notes)

In theory, it's still possible to have goroutines hog the CPU by never calling a non-inlined function, but such cases are much more rare than goroutines which never make blocking calls, which is what would get you prior to Go 1.2.

huangapple
  • 本文由 发表于 2014年1月14日 05:50:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/21102078.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定