2016-08-15 73 views
3

我由channel chapter of Rust by Example的輸出很困惑:如何通過示例在Rust中運行通道?

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

static NTHREADS: i32 = 3; 

fn main() { 
    // Channels have two endpoints: the `Sender<T>` and the `Receiver<T>`, 
    // where `T` is the type of the message to be transferred 
    // (type annotation is superfluous) 
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel(); 

    for id in 0..NTHREADS { 
     // The sender endpoint can be copied 
     let thread_tx = tx.clone(); 

     // Each thread will send its id via the channel 
     thread::spawn(move || { 
      // The thread takes ownership over `thread_tx` 
      // Each thread queues a message in the channel 
      thread_tx.send(id).unwrap(); 

      // Sending is a non-blocking operation, the thread will continue 
      // immediately after sending its message 
      println!("thread {} finished", id); 
     }); 
    } 

    // Here, all the messages are collected 
    let mut ids = Vec::with_capacity(NTHREADS as usize); 
    for _ in 0..NTHREADS { 
     // The `recv` method picks a message from the channel 
     // `recv` will block the current thread if there no messages available 
     ids.push(rx.recv()); 
    } 

    // Show the order in which the messages were sent 
    println!("{:?}", ids); 
} 

使用默認NTHREADS = 3,我得到了以下的輸出:

thread 2 finished 
thread 1 finished 
[Ok(2), Ok(1), Ok(0)] 

爲什麼在相反的順序for循環打印的println!("thread {} finished", id);thread 0 finished去哪了?

當我改變爲NTHREADS = 8,一些更神祕的事情發生了:

thread 6 finished 
thread 7 finished 
thread 8 finished 
thread 9 finished 
thread 5 finished 
thread 4 finished 
thread 3 finished 
thread 2 finished 
thread 1 finished 
[Ok(6), Ok(7), Ok(8), Ok(9), Ok(5), Ok(4), Ok(3), Ok(2), Ok(1), Ok(0)] 

打印順序讓我感到困惑,甚至更多的線程0總是缺少。如何解釋這個例子?

我在不同的計算機上試過這個,得到了同樣的結果。

+0

'NTHREADS = 8',我看*線程9完成*。關於如何發生的任何想法?代碼明顯無法處理簡單計數的事實似乎是一個更糟糕的問題。 – Shepmaster

+1

您是否將NTHREADS = 10而不是8設置爲輸出? –

回答

6

對於線程或它們之間的任何協調沒有保證順序,因此它們將以任意順序執行並將結果發送到通道。這就是完整的一點 - 如果它們是獨立的,則可以使用多個線程。

主線程從通道中拉出N值,將它們放入Vec,打印Vec並退出。

主線程不是等待子線程退出前完成。缺少的打印由最後一個子線程將值發送到通道中解釋,主線程讀取它(結束for循環),然後程序退出。線程從來沒有機會打印完成。

在主線程恢復和退出之前,最後一個線程有機會運行並打印出來也是可能的。

根據CPU或OS的數量,每種情況可能更多或更少,但都是正確程序的運行。

修改,以等待線程的代碼的版本顯示不同的輸出:

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

static NTHREADS: i32 = 3; 

fn main() { 
    // Channels have two endpoints: the `Sender<T>` and the `Receiver<T>`, 
    // where `T` is the type of the message to be transferred 
    // (type annotation is superfluous) 
    let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel(); 

    let handles: Vec<_> = (0..NTHREADS).map(|id| { 
     // The sender endpoint can be copied 
     let thread_tx = tx.clone(); 

     // Each thread will send its id via the channel 
     thread::spawn(move || { 
      // The thread takes ownership over `thread_tx` 
      // Each thread queues a message in the channel 
      thread_tx.send(id).unwrap(); 

      // Sending is a non-blocking operation, the thread will continue 
      // immediately after sending its message 
      println!("thread {} finished", id); 
     }) 
    }).collect(); 

    // Here, all the messages are collected 
    let mut ids = Vec::with_capacity(NTHREADS as usize); 
    for _ in 0..NTHREADS { 
     // The `recv` method picks a message from the channel 
     // `recv` will block the current thread if there no messages available 
     ids.push(rx.recv()); 
    } 

    // Show the order in which the messages were sent 
    println!("{:?}", ids); 

    // Wait for threads to complete 
    for handle in handles { 
     handle.join().expect("Unable to join"); 
    } 
} 

注意如何,在這種情況下,主線程打印以前最後一個線程退出:

thread 2 finished 
thread 1 finished 
[Ok(2), Ok(1), Ok(0)] 
thread 0 finished 

對於這四條線在約任何命令中發生也是有效的:在主線程打印之前或之後沒有任何子線程打印的理由。

+0

謝謝,這非常有幫助! – asdetrefle