16

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.

agnsaft
  • 1,791
  • 7
  • 30
  • 49

3 Answers3

15

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.

thwd
  • 23,956
  • 8
  • 74
  • 108
  • Thank you for your response. My concern is not how to get it to work, but rather how to make it as good as possible. In my mind there must be some sort of internal event notification loop, and connecting with this for my own blocking calls would seem like the best possible way for me to ensure good performance and scheduling? – agnsaft Jan 14 '14 at 07:30
  • I have amended my answer. – thwd Jan 14 '14 at 13:30
12

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.

Allen Luce
  • 7,859
  • 3
  • 40
  • 53
Not_a_Golfer
  • 47,012
  • 14
  • 126
  • 92
  • This is interesting, however, wouldnt reusing existing event loop be even more efficient? – agnsaft Jan 14 '14 at 10:40
  • depends on the number of such "blocking" goroutines. As of Go 1.2 I don't think you should have anything to worry about as goroutines yield execution even when not blocking on IO, and new goroutines preempt the scheduler (not sure if that's the right term but you get what I mean). I personally leave this stuff to the go scheduler, unless you're calling an external C library. – Not_a_Golfer Jan 14 '14 at 10:58
  • Calling an external C library is actually the use case for my original question. – agnsaft Jan 14 '14 at 11:43
  • then maybe locking the os thread is not such a bad idea if you're not planning on doing this for many goroutines. – Not_a_Golfer Jan 14 '14 at 12:52
  • Nitpick: you misspelled `Gosched` with `Goshced`. Outside of that, your answer helped me. Thanks. – dimitarvp Dec 29 '15 at 17:47
5

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.

joshlf
  • 21,822
  • 11
  • 69
  • 96
  • Thank you for your response. Although interesting and partially answers my question I still want to know how to make my custom blocking methods play in the nicest possible way with the scheduler? If the scheduler uses some sort of internal event notification loop it should be possible to use this for custom libraries as well? – agnsaft Jan 14 '14 at 07:29
  • 1
    If you want to force the scheduler to run, you can make a call to `runtime.Gosched()`. But it's probably not worthwhile unless you actually notice a problem. – James Henstridge Jan 14 '14 at 08:27
  • If you're using Go 1.2, you shouldn't have any issues unless you've got code that loops without making any function calls. Each function call gives the scheduler an opportunity to jump in and "preempt" the currently running goroutine. The compiler and runtime will figure out when to actually take that opportunity, but you should assume that it's good at making that decision intelligently. – joshlf Jan 14 '14 at 08:35