package main
func main() {
go println("I ran")
for {}
}
If you run with: GOMAXPROCS=1 go run main.go
It will never print the statement. This doesn't come up frequently in practice, but Linux does not suffer from this case.As a sibling correctly notes, `select {}` is what you want to do instead. You need to do something that can block the goroutine in such a way that control returns to the scheduler. Selecting is one of those ways.
1. Go's scheduler is better at scheduling Go. There are well defined points at which a context switch can occur, and the scheduler is in userland so you never really have expensive context switches.
2. Not sure. I would guess so, if only because it saves a couple context switches.
3. Goroutines are absolutely relevant for both. The stdlib webserver is a high-performance, production-grade implementation and it forks a goroutine for each request, and applications frequently fork more goroutines within a request handler.
4. I assume you're comparing Go channels to pipes? In which case the answer is probably always "faster", but how much faster depends on the size of the thing you're transferring since IPC requires serialization and deserialization while in Go the overhead is just some locking and a pointer copy.
5. Go has dynamically-sized stacks. While you can change stack size on Linux (to use very small stacks and thus spin up more threads), I don't think you can change the size of an individual stack. Plus with Go, you don't have to configure stack size at all; it works out of the box on every platform.
For me, that Go's goroutines are performant is just gravy. I like that they're a platform agnostic concurrency abstraction. I don't have to deal with sync vs async ("What color is your function"; which incidentally cost me the last 1.5 workdays tracking down a performance bug in Python) or posix threads vs Windows threads.
GCC also, on some platforms, allows using segmented stacks on any C and C++ programs which means that a thread will only use only as much physical and virtual memory as required. I don't think segmented stacks have seen much use though as they have non zero cost and, for optimal space usage, ideally require recompiling any dependncy, including glibc with them.
Interestingly segmented stacks were implemented to support GCCGO.
This took some of the bloom off the rose for me. For each goroutine I had to predict ahead-of-time whether it would require a kernel thread, and if so send it through a rate limiter. Effectively it was sync-vs-async but hidden.
An expected way to manage this would be to have a limit on the number of threads to run blocking syscalls. Up to you how to do it (threadpool for syscalls, anythrrad can run a syscalls but checks a mutex first, etc)
I don't think there's a danger of deadlocks here -- your blocking syscalls shouldn't be dependant on other goroutines. Eventually the calls will succeed or timeout (one hopes) and the next call will commence.
In my experience, you can usually find a safe number of parallel syscalls to run -- and it's often not very many.
https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part...
To be more specific, most socket calls should be async + selectish, but file system calls would likely be done as synchronous, because async doesn't generally work for that -- anyway limiting the number of outstanding file I/O requests tends to help throughput.
https://stackoverflow.com/questions/41627931/in-golang-packa...
https://www.techempower.com/benchmarks/
http://marcio.io/2015/07/handling-1-million-requests-per-min...
It can be possible only if there are no more "physical" threads than cores. And anyway a switching between async routines on the same "physical" thread requires a switching of routines contexts (go to the memory and update cashes).
They are definitely not more performant than native threads with a work-stealing approach (producer - consumer pattern) that is tuned to the number of cores available. How could they be more performant? Even the simplest switching adds some cost, from a performance perspective it would never make sense to run thousands of threads on 8 cores, whether they are scheduled by the OS or by the language implementation.
So to answer your 3rd question: short lived I/O tasks.
I keep expecting someone to write one and put it up on NPM (I haven't looked recently, though). Perhaps I should clean up my and stick it up there. It provides a very nice abstraction of continuations. I did it merely as kata to show that continuations do not require concurrency.
https://github.com/olahol/node-csp
Are you asking to compare mutexes and channels? The article goes through multiple reasons why the go scheduler has more information about how something should execute than the Linux kernel. Reread and then maybe rephrase?
1. is Go scheduler better than Linux scheduler if you have a thousand concurrent goroutines or threads?
2. is really creating goroutines that much faster than creating a native thread?
3. are gorutines relevant for long lived and computationally intensive tasks or just for short lived I/O task?
4. What is the performance of using channels between goroutines compared to native threads?
tbh, I have read several of respected articles that criticize Golang goroutines in terms of performance and I am not really sure that Golang's only virtue imho which is simple concurrency is performant at all