首页 > 解决方案 > Hanging in channel receiver iterator in Rust?

问题描述

The following code hangs in iterator: (playground)

#![allow(unused)]
fn main() {
    use std::sync::mpsc::channel;
    use std::thread;
    
    let (send, recv) = channel();
    let num_threads = 3;
    for i in 0..num_threads {
        let thread_send = send.clone();
        thread::spawn(move || {
            loop { // exit condition does not matter, breaking right after the 1st iteration
                &thread_send.send(i).unwrap(); // have to borrow in the loop
                break;
            }
            println!("thread {:?} finished", i);
        });
    }

    // drop `send` needed here (as it's cloned by all producers)?
    
    for x in recv { // hanging
        println!("Got: {}", x);
    }
    println!("finished iterating");
}

In the output we can clearly see the threads exited (so thread-local cloned senders are dropped):

thread 0 finished
Got: 0
Got: 1
Got: 2
thread 1 finished
thread 2 finished

finished iterating is never printed and the process is interrupted in playground (hangs forever locally).

What's the reason?

PS. Loop in the threads is required (it's a simplified example of the code that is actually used) to show the real use case.

标签: multithreadingrustconcurrencychannel

解决方案


You need to drop send at the location indicated by your comment because the channel is only closed when the last sender is dropped. The main thread's send will only be dropped when it goes out of scope which is at the end of the function.

You can either call drop(send) explicitly or restructure your code so that the main thread's send goes out of scope before you start our receive loop as Stargateur pointed out in the comments (playground). This is preferrable because it is immediately clear to the reader where send is dropped while the drop(send) statement can be easily missed.


推荐阅读