「Rust中的多线程」3

1,181 阅读3分钟

「这是我参与11月更文挑战的第 2 天,活动详情查看:2021最后一次更文挑战


Channel

确保并发安全的一个流行方法是消息传递。多个线程通过互相发送包含数据的消息进行通信。

Rust为此提供了一个 channel

你可以把 channel 想象成一条水流:把东西放进一端,它就会从另一端出来。

一个编程通道有两部分:一个发送器和一个接收器 → 把东西放进发送器,把东西从接收器中取出。

Rust标准库有一个这样的实现,叫做 mpsc,代表 "多生产者,单消费者" 。所以mpsc通道可以有多个发送方,但只有一个接收方。

你可以把它想象成一个有许多小河的三角洲,在同一地点结束。

std::sync::mpsc::channel 返回一个带有发送方和接收方的元组。

  1. 发送器可以被克隆,创建多个能够跨线程发送的副本。
  2. 发送器有一个发送方法,即:在通道上发送一个值。
  3. 发送的值必须拥有其所有权:一个发送的值不能再用于它所发送的线程中。当接收方收到该值时,所有权将转移到接收方。

你可以把 channel 看作是一个单一的所有权结构。

Receiver 有一个 recv() ,它可以阻塞当前线程,直到收到消息。

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

fn main() {
    let (sender, receiver) = mpsc::channel();
    for i in 0..10 {
        let sender = sender.clone();
        thread::spawn(move|| {
            sender.send(i).unwrap();
        });
    }

    for _ in 0..10 {
        // receive each value and wait between each one
        println!("Got: {}", receiver.recv().unwrap());
    }
}

如果所有的发送者或单一的接收者被丢弃,通道就会关闭。

可以对接收器进行迭代。当迭代器访问下一个值时,接收器将被阻塞。当通道被关闭时,迭代器将返回 None 并结束。

如果我们将一个克隆的发送者发送到一个线程中,这就带来了一个小问题。原始的发送者永远不会被丢弃,而通道将保持开放 (有可能出现泄漏)

在上面的例子中,不存在这个问题,因为我们在上面循环了设定的次数,但是如果我们使用迭代器方法,就会产生一个无限的等待。

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

fn main() {
    let (sender, receiver) = mpsc::channel();
    for i in 0..10 {
        let sender = sender.clone();
        thread::spawn(move|| {
            sender.send(i).unwrap();
        });
    }

    // this will wait until all senders are dropped
    // the original sender is never dropped, so this waits forever
    for received in receiver {
        println!("Got: {}", received);
    }
}

解决办法是 drop 原来的发件人:

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

fn main() {
    let (sender, receiver) = mpsc::channel();
    for i in 0..10 {
        let sender = sender.clone();
        thread::spawn(move|| {
            sender.send(i).unwrap();
        });
    }

    // drop the original sender
    mem::drop(sender);

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

你也可以通过使用 大括号{} 将整个顶部部分包裹在一个范围内,确保在迭代器被调用时所有东西都离开范围 (Rust块级作用域)