Rust学习笔记(下)

前言

笔记的内容主要参考与《Rust 程序设计语言》,一些也参考了《通过例子学 Rust》和《Rust语言圣经》。

Rust学习笔记分为上中下,其它两个地址在Rust学习笔记(上)和Rust学习笔记(中)。

并发

线程

在大部分现代操作系统中,已执行程序的代码在一个 进程process)中运行,操作系统则负责管理多个进程。在程序内部,也可以拥有多个同时运行的独立部分。运行这些独立部分的功能被称为 线程threads)。

将程序中的计算拆分进多个线程可以改善性能,因为程序可以同时进行多个任务,不过这也会增加复杂性。因为线程是同时运行的,所以无法预先保证不同线程中的代码的执行顺序。这会导致诸如此类的问题:

  • 竞争状态(Race conditions),多个线程以不一致的顺序访问数据或资源
  • 死锁(Deadlocks),两个线程相互等待对方停止使用其所拥有的资源,这会阻止它们继续运行
  • 只会发生在特定情况且难以稳定重现和修复的 bug
创建线程

需要调用 thread::spawn 函数并传递一个闭包

use std::thread;
use std::time::Duration;

fn main() {
   
    //可以不赋给一个变量(主要为了后面)
    let handle = thread::spawn(|| {
   
        for i in 1..10 {
   
            println!("hi number {} from the spawned thread!", i);
            // 调用强制线程停止执行一小段时间,这会允许其他不同的线程运行。这些线程可能会轮流运行,不过并不保证如此:这依赖操作系统如何调度线程。
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
   
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    // 由于无法无法保证线程运行的顺序,很有可能在handle还没执行完,主线程就走到了最后,结束了线程
    // thread::spawn的返回值类型是JoinHandle。它是一个拥有所有权的值,当对其调用join方法时,会阻塞当前线程直到 handle 所代表的线程结束
    // 如果把这句话放到for i in 1..5这句前面,就会看到并不会交替输出,而是输出完handle里的,才走下面
    handle.join().unwrap();
}
move

其经常与 thread::spawn 一起使用,因为它允许我们在一个线程中使用另一个线程的数据。

use std::thread;

fn main() {
   
    let v = vec![1, 2, 3];

    let handle = thread::spawn(move || {
   
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

为什么传值要用move,也就是捕获所有权?

可以避免数据竞争,比如别人也调用了,然后更改了数据。还比如主线程在let handle下面drop了v,结果drop先执行了,那么handle也会发生错误

为什么thread::spawn要用这种闭包的写法?

首先可以利用闭包的一些特性吧,比如move,后面也不用写变量什么的,直接自动全拿,函数的话还得一个一个写

为什么let handle这里直接当成个函数?

因为人家就是这么设计的,这样就能handle.join().unwrap();,而且你也可以单独写个函数包起来

消息传递

通过发送包含数据的消息来相互沟通,这个思想来源于 Go 编程语言文档中 的口号:“不要通过共享内存来通讯;而是通过通讯来共享内存。” Rust 中一个实现消息传递并发的主要工具是 通道channel

编程中的通道有两部分组成,一个发送者(transmitter)和一个接收者(receiver)。当发送者或接收者任一被丢弃时可以认为通道被 关闭closed)了。

这里使用 mpsc::channel 函数创建一个新的通道;mpsc多个生产者,单个消费者multiple producer, single consumer)的缩写。简而言之,Rust 标准库实现通道的方式意味着一个通道可以有多个产生值的 发送sending)端,但只能有一个消费这些值的 接收receiving)端。

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

fn main() {
   
    // 第一个元素是发送端(transmitter),而第二个元素是接收端(receiver)
    let (tx, rx) = mpsc::channel();
    // 如果想要实现多个发送者,直接clone即可
		// let tx1 = tx.clone();
    thread::spawn(move || {
   
        let val = String::from("hi");
        tx.send(val).unwrap();
    });
		// 这个方法会阻塞主线程执行直到从通道中接收一个值。一旦发送了一个值,recv会在一个Result中返回它。当通道发送端关闭,recv会返回一个错误表明不会再有新的值到来了。
    let received = rx.recv().unwrap();
    // try_recv不会阻塞,相反它立刻返回一个Result Ok值包含可用的信息,而Err值代表此时没有任何消息。如果线程在等待消息过程中还有其他工作时使用try_recv很有用:可以编写一个循环来频繁调用try_recv,在有可用消息时进行处理,其余时候则处理一会其他工作直到再次检查。
    println!("Got: {}", received);
}

可以发送多个值

use std::thread;
use std::sync::mpsc;
use std::time::Duration

你可能感兴趣的:(Rust,rust)