8 ms·
Green Threads Explained
- stygiansonic 9y agoRelated: Early (almost prehistoric by software timeline standards) versions of Java used a green thread implementation, but this was dropped in Java 1.1 in favour of the current native OS thread model. Some background: https://softwareengineering.stackexchange.com/questions/120384/why-not-green-threads https://softwareengineering.stackexchange.com/questions/1203...
- marcosdumay 9y agoJava had a quite lousy implementation of green threads, that hurt performance more than enabled parallelism. (What isn't explainable by it being early, since there were earlier green threads implementation that didn't suck. Looks like it just wasn't a priority for Java developers.) There's nothing anywhere restricting green threads to a single OS thread. Most modern runtimes will automatically multiplex the green threads into as many OS threads as your computer can run.
- dragonwriter 9y ago> Java had a quite lousy implementation of green threads, that hurt performance more than enabled parallelism 1:N green threads (which Java had) aren't intended for parallelism and provide none. They provide concurrency only. M:N green threads (e.g., Erlang processes) provide parallelism.
- DamonHD 9y agoJava had a mixed model where it could use LWPs or kernel threads, and switched over around Solaris 8 because things like stat() were still blocking and had no async counterpart, so were difficult to deal with. I dug up this fossil the other day, trying to get a fix from Sun for this very issue: http://d.hd.org/JavaThreadsCommentToSun19980820.html http://d.hd.org/JavaThreadsCommentToSun19980820.html
- ridiculous_fish 9y agoIs this solved in Go? A while back Go required programmers to use rate limiters, otherwise it would spawn unlimited kernel threads for blocking syscalls.
- 4ad 9y agoIt's capped at 10000 threads (configurable). Note that Go uses asynchronous I/O where possible.
- fulafel 9y agoI think Java only started shipping native Linux thread support somewhat later, maybe in JDK 1.3.
- dboreham 9y agoLinux itself didn't have (working) threads until around 2003.
- fulafel 9y agoLinux got threads with the clone() system call in 1999. They worked but it was not a cross platform API. Though in that time most operating systems had proprietary thread APIs. Standards compliant POSIX threads came later to Linux than some other Unix systems, that much is true.
- cpeterso 9y agoRust originally supported only M:N green threads. Native thread support was added, became the default, and green thread support was eventually moved out of Rust core to a library. Here is the proposal and rationale: https://github.com/rust-lang/rfcs/blob/0806be4f282144cfcd55b1d20284b43f87cbe1c6/text/0230-remove-runtime.md https://github.com/rust-lang/rfcs/blob/0806be4f282144cfcd55b...
- jerryr 9y agoInteresting. I hadn't heard the "green thread" term before. Does anyone happen to know how this compares to Windows' "fiber" mechanism? I can't recall whether fibers actually run in user land.
- roblabla 9y agoA Fiber is a cooperative multitasking strategy. As such, a fiber must yield at some point to allow other fibers to do their work. Green threading is just the idea of doing the scheduling of threads in user-space. It can be preemptive or cooperative. Fibers and green threading aren't mutually exclusive, afaik.
- baruch 9y agoAre there actual examples of green threads that are not cooperative? All the implementations I've seen are cooperative.
- chrisseaton 9y agoHaskell has green (N to M) threads that are pre-emptive. As does Go (but it calls them coroutines). Erlang (but it calls them processes) I'm not sure about. Maybe it only re-schedules on message sends and receives?
- derefr 9y agoErlang does a different thing called "reduction-counting": the active process in a scheduler-thread gets a budget of virtual CPU cycles (reductions/"reds"), tracked in a VM register, and part of the implementation of each op in the VM's ISA is to reduce that reduction-count by an amount corresponding to the estimated time-cost of the op. Then, the implementation of the call and ret ops in the ISA both check the reduction-counter, and sleep the process (scheduling it to resume at the new call-site) if it has expended all its reds. (If you're wondering, this works to achieve soft-realtime guarantees because, in Erlang, as in Prolog, loops are implemented in terms of recursive tail-calls. So any O(N) function is guaranteed to hit a call or ret op after O(1) time.) If you're writing an Erlang extension in C (a "NIF"), though, and your NIF code will be above O(1), then you have to ensure that you call into the runtime reduction-checker yourself to ensure nonblocking behavior. In that sense, Erlang is "cooperative under the covers"—you explicitly decide where to (offer to) yield. It's just that the Erlang HLL papers over this by having one of its most foundational primitives do such an explicit yield-check.
- api 9y agoIt's occurred to me that asynchronous programming is essentially just very lightweight green threads with the added benefit of scheduling tied directly to I/O. Synchronous programming is typically much more natural. It's too bad no languages have opted to abstract this away. Of course I suppose coroutines are kind of that.
- zambal 9y ago>It's too bad no languages have opted to abstract this away. Erlang and other BEAM languages like Elixir and LFE kind of do. They use the actor model for concurrency and message passing is asynchronous. However, most of the time directly after sending a message, a program waits for a reply message, which blocks the calling process until the reply arrives, or a configurable timeout passes, making it a synchronous call. This is ok since spawning new processes is very cheap, so a web server for example idiomaticly spawns a new process for every request, making blocking calls in one request not interfere with other requests. The result is highly concurrent code that is mostly written synchronously without creating red function/ blue function kind of divisions most(?) async/await implementations have.
- deleted 9y ago[deleted]
- reddit_clone 9y agoErlang does that. The logic inside erlang processes look synchronous but outwardly its all async. For example you can send a message to a different process, even running on a different machine and wait for the reply right there in the next line. It doesn't hold any OS threads.
- he0001 9y agoIntriguing thought, async to what? If a program needs the input before continuing doesn't it need to wait and therefore hold the program flow and therefore stop, even in erlang?
- reddit_clone 9y ago
- deleted 9y ago[deleted]
- theprotocol 9y agoI enjoyed the expedient writing style. The author did a great job of expressing the essential knowledge without too much verbosity. It's very clear what the skeleton of the project consists of, and the known unknowns are labeled (i.e. "this is how this works, you can read more later [here]"). Some may find this directness unempathetic, but I consider it as the opposite: the writing seems precisely aware of exactly what the reader wants to know at any given time and what questions he or she would be likely to have on their mind.
- afandian 9y agoCurious about which bit someone might find unempathetic? It all seems pretty straightforward to me.
- theprotocol 9y agoI just meant it subjectively. I assume some might not like this expedient style as a matter of taste, because most publications/articles/tutorials are not written so concisely.
- spc476 9y agoI recently did a similar thing [1]. The major difference I did was to store the registers on the stack instead of a separate structure. The stack is the thread structure. I also have a version for x86-32 bit and x86-64 bit (and they work under Linux and Mac OS-X). The assembly code isn't much, but it took a while to work out just what was needed and no more. [1] http://boston.conman.org/2017/02/27.1 http://boston.conman.org/2017/02/27.1
- ndesaulniers 9y agoIn the code listing on the 4th page [0], why is MaxGThreads and StackSize wrapped in an enum? I would use an enum when I might want some values to be mutually exclusive. Seems like these values aren't even used in the same context? I would use static const ints. [0] https://c9x.me/articles/gthreads/code0.html https://c9x.me/articles/gthreads/code0.html
- caf 9y agoBecause in C (as opposed to C++), a static const int is not a compile-time constant (so you can't use it in places like switch case labels).