Process never exits after successfully receiving from MPSC channel
Asked Answered
D

2

5

Here's the code:

use std::thread;
use std::sync::mpsc;

fn main() {

    //spawn threads 
    let (tx, rx) = mpsc::channel();
    for mut i in 0 .. 10 {
        let txc = tx.clone();   //clone from the main sender
        thread::spawn( move || {            
            i += 20;
            println!("Sending: {}", i);
            txc.send(i).unwrap_or_else(|e| {
                eprintln!("{}", e);
            });
        });
    }

    for received in rx {
        println!("Received: {}", received);
    }

}

The code runs successfully but it hangs and the process never exits at the end.

I thought it could be related to closing the channel ends and I tried dropping by tx.drop() and rx.drop() but the compiler gave an error.

What am I doing wrong here?

Derron answered 22/4, 2020 at 12:17 Comment(0)
D
10

tx in your main thread is not dropped until the end of the main function, and rx will not be closed until all senders have been dropped.

To fix this, you can manually drop it with drop(tx) after you have started all of your threads:

use std::thread;
use std::sync::mpsc;

fn main() {

    //spawn threads 
    let (tx, rx) = mpsc::channel();
    for mut i in 0 .. 10 {
        let txc = tx.clone();   //clone from the main sender
        thread::spawn( move || {            
            i += 20;
            println!("Sending: {}", i);
            txc.send(i).unwrap_or_else(|e| {
                eprintln!("{}", e);
            });
        });
    }

    // drop tx manually, to ensure that only senders in spawned threads are still in use
    drop(tx);

    for received in rx {
        println!("Received: {}", received);
    }

}
Disillusionize answered 22/4, 2020 at 13:16 Comment(0)
D
1

All senders need to go out of scope before the receiver. If both goes out of scope simultaneously, the program gets stuck in a deadlock => main thread waits for the receiver to exit, while the receiver waits for the sender to exit (which has already exited).

This worked for me =>

use std::thread;
use std::sync::mpsc;

fn main() {

 // returning rx from the inner scope to preserve its scope.
 let rx = {
    // spawn threads
    let (tx, rx) = mpsc::channel();
    for mut i in 0 .. 10 {
        let txc = tx.clone();   //clone from the main sender
        thread::spawn( move || {            
            i += 20;
            println!("Sending: {}", i);
            txc.send(i).unwrap_or_else(|e| {
                eprintln!("{}", e);
            });
        });
    }
    rx
    // tx goes out of scope here.
 };

 for received in rx {
    println!("Received: {}", received);
 }
// rx goes out of scope here.
}
Denounce answered 22/10 at 10:3 Comment(2)
what did you do in your code to prevent the deadlock? Adding a code comment or explanation on where you've changed things will improve your answer :)Gurley
Done. Thanks for the feedback.Denounce

© 2022 - 2024 — McMap. All rights reserved.