Why do threads spawned by thread::spawn show up as green threads?

Hello all

So I was wondering why when I filter for the threads created by thread::spawn show up as green threads in htop? A minimal working example is -

use std::thread;
use std::time::Duration;
use std::sync::mpsc;

fn main() {
    let (tx, rx) = mpsc::channel();
    let tx2 = tx.clone();

    // Create two threads and print the outputs
    let t1 = thread::spawn(move || {
        let msgs = vec![String::from("First thread msg 1"),
            String::from("First thread msg 2"),
            String::from("First thread msg 3"),
            String::from("First thread msg 4")];
        for val in msgs {
    let t2 = thread::spawn(move || {
        let msgs = vec![
            String::from("Second thread msg 1"),
            String::from("Second thread msg 2"),
            String::from("Second thread msg 3"),
            String::from("Second thread msg 4"),

        for val in msgs {

    for r in rx {
        println!("Got: {}", r);


It produces the output expected -

mintycocoa@TheTreehouse:/tmp$ ./mwe_threads
Got: Second thread msg 1
Got: First thread msg 1
Got: Second thread msg 2
Got: First thread msg 2
Got: Second thread msg 3
Got: First thread msg 3
Got: Second thread msg 4
Got: First thread msg 4

However during it's execution, if I filter htop by the text mwe_threads and see the processes I get -

One real process with two green threads. And just to ensure that this is in fact a green thread, I told htop to hide all green (user) threads by pressing H and I get -

I've heard that rust only has OS threads (so 1-to-1 threads or processes), but it seems to be creating green threads. I'm just beginning learning concurrency in rust, so I may be misunderstanding. Could someone explain why I'm seeing this?

I think you misunderstood the legend. The green sub-entries are (real, OS) threads, the white ones are processes.


What htop shows are standard threads (as in, on POSIX systems, created by the pthread API). Green threads in the sense used originally by early Java versions and later by Go among other languages, are a custom construct managed by the language runtime and cannot be seen by tools like htop.


Thank you for the explanation! Just to check if I understand correctly, these threads are independent threads which may be running on different cores and have different blocks of memory allocated to them by the OS. Is that correct?

Sure. They are in fact so independent that historically, the pthread mechanism on Linux used to spawn a complete new process. (This is not the case anymore, AFAIK.)

Yes, they're "real" threads with their own stack and managed by the OS scheduler. The distinction htop makes is between user-space threads launched by applications but still scheduled etc by the OS, and kernel threads spawned by the kernel to do kernel things in kernel space.

Ah okay. Thank you for clearing it up! Your help is greatly appreciated! :heart:

It's matter of terminology, mostly. Initially Linux had no threads at all, instead it was possible to make tasks share memory. Unfortunately this model was completely incompatible with how POSIX wanted to handle signals (because POSIX, at that time, basically meant Solaris and Solaris used different model).

To satisfy these requirements threads got additional bit which marked some of them as threads and some of them as processes. Except for signal delivery they are the same thing, though.

This means, in particular, that it's possible, on Linux, to create “threads” which wouldn't share memory, would have separate tables of file descriptors, etc. That's highly not recommended, though, because this may confuse various tools greatly.

To this day, within the Linux kernel every thread has a unique "process" ID (pid), while the userspace concept of a process is the thread group ID (tgid).


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.