0

Given the following code:

use std::sync::mpsc::{channel, Sender, Receiver};
use std::thread;

fn transceiver(
    tx: Sender<u32>,   tx_string: &str,
    rx: Receiver<u32>, rx_string: &str,
) {
    let message_count = 3;
    for message in 0..message_count {
        println!("message {}: {}", message, tx_string);
        tx.send(message).unwrap();
        println!("message {}: {}", rx.recv().unwrap(), rx_string);
    }
}

fn main() {

    let (atx, arx) = channel();
    let (btx, brx) = channel();

    thread::spawn(move || {
        transceiver(atx, "A --> B", brx, "A <-- B");
    });

    thread::spawn(move || {
        transceiver(btx, "B --> A", arx, "B <-- A");
    });
}

I get no output. I had to add a delay at the end of main:

std::old_io::timer::sleep(std::time::duration::Duration::seconds(1));

After that, I get this output:

message 0: B --> A
message 0: A --> B
message 0: A <-- B
message 0: B <-- A
message 1: B --> A
message 1: A --> B
message 1: A <-- B
message 2: A --> B
message 1: B <-- A
message 2: B --> A
message 2: B <-- A
message 2: A <-- B

The doc says these threads should outlive their parents, but here it seems they die as soon as the parent (in this case, main), dies.

tshepang
  • 12,111
  • 21
  • 91
  • 136
  • possible duplicate of [Why do some statements not execute when they are run in a task?](http://stackoverflow.com/questions/27677466/why-do-some-statements-not-execute-when-they-are-run-in-a-task) – Shepmaster Jan 04 '15 at 20:48

1 Answers1

4

The doc says these threads should outlive their parents, but here it seems they die as soon as the parent (in this case, main), dies.

This does not apply to the main thread; the program ends once the main thread is finished.

What you want to do is have the main thread wait until the other threads finish, i.e. you want to "join" the child thread to the main thread. See the join method for this.

let (atx, arx) = channel();
let (btx, brx) = channel();

let guard0 = thread::scoped(move || {
    transceiver(atx, "A --> B", brx, "A <-- B");
});

let guard1 = thread::scoped(move || {
    transceiver(btx, "B --> A", arx, "B <-- A");
});

guard0.join();
guard1.join();

Note that the calls to join are implicit when the JoinGuard drops, but they're made explicit here for illustration.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Jorge Israel Peña
  • 36,800
  • 16
  • 93
  • 123
  • What does *outlive* mean in the docs? Also, how would you modify my code so it works with `join`? – tshepang Jan 04 '15 at 21:19
  • It means what you think it means, it's just that the main thread is special and different from the rest. No other thread may outlive the main thread; the program ends once the main thread is finished. I linked the documentation to the `join` method. It returns a `Result` that may contain the result of the thread, if any. In your case you no result, i.e. `()`. So you call `join` instead of detach, and you can then inspect the value returned by `join` if you like, to determine whether the thread failed for any reason. – Jorge Israel Peña Jan 04 '15 at 21:27
  • Replacing `detach` with `join` results in a program that displays `message 0: A --> B`, and then waits forever. – tshepang Jan 04 '15 at 22:20
  • Remove the calls to `detach` or `join`, so that you're left with the `JoinGuard` for each one. Then after the calls to spawn, call `join` on each of the guards. The call to `join` waits until that thread finishes before continuing, so by joining on the first one immediately, the second thread doesn't start until the first one finishes, and the first one can't finish because the second one hasn't started for it to interact with. – Jorge Israel Peña Jan 04 '15 at 22:47
  • By the way, the explicit calls to join --- the last two lines in my example --- are not necessary. They are implicitly called when the JoinGuard is dropped. In other words, you can remove the last two lines and it should work as intended, as long as you don't explicitly call detach like you did in your original code. – Jorge Israel Peña Jan 06 '15 at 00:19
  • It does indeed, though I get build warnings about unused variables. – tshepang Jan 06 '15 at 01:07
  • Try not binding them to a variable, i.e. remove the "let t1 =" parts. If that doesn't work, then prepend the name of the variable with an underscore, e.g. _t1 – Jorge Israel Peña Jan 06 '15 at 01:11
  • I had to prefix the variables with underscores. The other method didn't work. – tshepang Jan 06 '15 at 09:00
  • The edit you rolled back was fixing compiler errors made by a younger rustc. – tshepang Jan 10 '15 at 10:48