rust学习-线程

这篇具有很好参考价值的文章主要介绍了rust学习-线程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Rust 标准库只提供了 1:1 线程模型
Rust 是较为底层的语言,如果愿意牺牲性能来换取抽象,以获得对线程运行更精细的控制及更低的上下文切换成本,使用实现了 M:N 线程模型的 crate

示例

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

fn main() {
    // 调用 thread::spawn 函数并传递一个闭包,来创建线程
    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));
    }

    // 等待结束
    // // `unwrap` 在接收到 `None` 时将返回 `panic`
    handle.join().unwrap();
}

在一个线程中使用另一个线程的数据

在参数列表前使用 move 关键字强制闭包获取其使用的环境值的所有权。
这个技巧在创建新线程将值的所有权从一个线程移动到另一个线程时最为实用

use std::thread;

fn main() {
    let v = vec![1, 2, 3]
    
    let handle = thread::spawn(|| {
        // `v` is borrowed here
        // Rust 不知道这个新建线程会执行多久,所以无法知晓 v 的引用是否一直有效
        println!("Here's a vector: {:?}", v);
    });

	// 一个具有闭包的线程,尝试使用一个在主线程中被回收的引用 v
	// drop(v);

    handle.join().unwrap();
}

force the closure to take ownership of v (and any other referenced variables), use the move keyword

use std::thread;

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

    // 增加 move 关键字,强制闭包获取其使用的值的所有权
    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
    // value borrowed here after move
    println!("Here's a vector: {:?}", v); // 这里编译报错
}
use std::thread;

fn main() {
    let v = vec![1, 2, 3]
    
    let handle = thread::spawn(move || {
        // `v` is borrowed here
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

channel

mpsc:multiple producer, single consumer

基本用法

use std::thread;
// 多发单收模型
// multiple producer, single consumer
use std::sync::mpsc;

fn main() {
	// tx 是发送端,rx是接收端
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
    });

	// 主线程阻塞
    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

try_recv 不会阻塞,相反它立刻返回一个 Result<T, E>:Ok 值包含可用的信息,而 Err 值代表此时没有任何消息

示例

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

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let vals = vec![
            String::from("hi"),
            String::from("from"),
            String::from("the"),
            String::from("thread"),
        ];

        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

	// 打印出四个消息后退出
    for received in rx {
        println!("Got: {}", received);
    }
}

所有权被转移

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

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();
        // 编译失败
        //  value borrowed here after move
        println!("val is {}", val);
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}

多生产者

通过克隆通道的发送端来做到
类似于Go中的:“不要通过共享内存来通讯;而是通过通讯来共享内存
(“Do not communicate by sharing memory; instead, share memory by communicating.”)

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

fn main() {
    let (tx, rx) = mpsc::channel();

    let tx1 = tx.clone();
    thread::spawn(move || {
	let vals = vec![
	    String::from("hi"),
	    String::from("from"),
	    String::from("the"),
	    String::from("thread"),
	];

	for val in vals {
	    tx1.send(val).unwrap();
	    thread::sleep(Duration::from_secs(1));
	}
    });

    thread::spawn(move || {
	let vals = vec![
	    String::from("more"),
	    String::from("messages"),
	    String::from("for"),
	    String::from("you"),
	];

	for val in vals {
	    tx.send(val).unwrap();
	    thread::sleep(Duration::from_secs(1));
	}
    });

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

    println!("finish")
}

Mutex 互斥器

使用方式

cat main.rs
use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);

    {
        // lock 调用 返回 一个叫做 MutexGuard 的智能指针
        // 这个智能指针实现了 Deref 来指向其内部数据
        // 也提供了一个 Drop 实现当 MutexGuard 离开作用域时自动释放锁
        let mut num = m.lock().unwrap();
        *num = 6;
    }

    // 打印内容如下 Mutex { data: 6, poisoned: false, .. }
    println!("m = {:?}", m);
}

糟糕例子

use std::sync::Mutex;
use std::thread;

fn main() {
    let counter = Mutex::new(0);
    let mut handles = vec![];

    for _ in 0..10 {
        // value moved into closure here, in previous iteration of loop
        // 编译失败
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();

            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}

解决方式-Arc

Arc 和 Rc 有着相同的 API
Arc 一个类似 Rc 并可以安全的用于并发环境的类型
字母 “a” 代表 原子性(atomic),所以这是一个原子引用计数

为什么不是所有标准库中的类型都默认使用 Arc 实现?
线程安全带有性能惩罚,只在必要时才为此买单

use std::sync::{Mutex, Arc};
use std::thread;

fn main() {
    // counter 是不可变的,因为前面没有mut
    // 意味着Mutex<T> 提供了内部可变性
    // 就像使用 RefCell<T> 可以改变 Rc<T> 中的内容那样
    // 使用 Mutex<T> 来改变 Arc<T> 中的内容 
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();

            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

	// 打印结果为10
    println!("Result: {}", *counter.lock().unwrap());
}

Mutex 也有造成 死锁(deadlock) 的风险

使用 Sync 和 Send trait 的可扩展并发

语言本身对并发知之甚少。上述讨论的几乎所有内容都属于标准库,而不是语言本身的内容,可以编写自己的或使用别人编写的并发功能。
但有两个并发概念是内嵌于语言中的:std::marker 中的 Sync 和 Send trait

通过 Send 允许在线程间转移所有权

【Send 标记 trait】
表明类型的所有权可以在线程间传递
几乎所有的 Rust 类型都是Send 的,但是也有例外,比如Rc
如果克隆了 Rc 的值并尝试将克隆的所有权转移到另一个线程,这两个线程都可能同时更新引用计数,Rc 被实现为用于单线程场景,所以不用担心多线程下的引用计数问题。

糟糕例子

the trait Send is not implemented for Rc<Mutex<i32>>

use std::rc::Rc;
use std::sync::Mutex;
use std::thread;

fn main() {
    let counter = Rc::new(Mutex::new(0));
    let mut handles = vec![];

    // Rc<Mutex<i32>>` cannot be sent between threads safely
    for _ in 0..10 {
        let counter = Rc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();

            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}

解决办法

使用原子引用计数 Arc

Sync 允许多线程访问

【Sync 标记 trait】
一个实现了 Sync 的类型可以安全的在多个线程中拥有其值的引用
对于任意类型 T,如果 &T(T 的引用)是 Send 的话 T 就是 Sync 的
其引用就可以安全的发送到另一个线程

Rc 不是 Sync的
RefCell 和 Cell 系列类型不是 Sync 的
RefCell 在运行时所进行的借用检查也不是线程安全的
Mutex 是 Sync 的,可以被用来在多线程中共享访问文章来源地址https://www.toymoban.com/news/detail-600298.html

到了这里,关于rust学习-线程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Rust】Rust学习 第十三章Rust 中的函数式语言功能:迭代器与闭包

    Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是  函数式编程 ( functional programming )。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。 更具体的,我们将要涉及: 闭包 ( Closures ),一个可以储

    2024年02月12日
    浏览(53)
  • Rust 程序设计语言学习——基础语法

    Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。 Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软

    2024年01月22日
    浏览(52)
  • 【从零开始的rust web开发之路 一】axum学习使用

    第一章 axum学习使用 本职java开发,兼架构设计。空闲时间学习了rust,目前还不熟练掌握。想着用urst开发个web服务,正好熟悉一下rust语言开发。 目前rust 语言web开发相关的框架已经有很多,但还是和java,go语言比不了。 这个系列想完整走一遍web开发,后续有时间就出orm,还

    2024年02月12日
    浏览(54)
  • 不同开发语言在进程、线程和协程的设计差异

    在多线程项目开发时,最常用、最常遇到的问题是 1,线程、协程安全 2,线程、协程间的通信和控制 本文主要探讨不同开发语言go、java、python在进程、线程和协程上的设计和开发方式的异同。 进程 进程是 操作系统进行资源分配的基本单位,每个进程都有自己的独立内存空

    2024年01月22日
    浏览(38)
  • Java/Python/Go不同开发语言在进程、线程和协程的设计差异

    在多线程项目开发时,最常用、最常遇到的问题是 1,线程、协程安全 2,线程、协程间的通信和控制 本文主要探讨不同开发语言go、java、python在进程、线程和协程上的设计和开发方式的异同。 进程 进程是 操作系统进行资源分配的基本单位,每个进程都有自己的独立内存空

    2024年01月23日
    浏览(50)
  • 【Rust 基础篇】Rust 多线程:并发编程的艺术

    多线程是现代计算机编程中的重要概念,它允许程序同时执行多个任务,充分利用多核处理器的性能优势。在 Rust 中,多线程编程也得到了很好的支持,通过标准库提供的 std::thread 模块可以方便地创建和管理线程。本篇博客将详细介绍 Rust 中多线程的使用方法,包含代码示例

    2024年02月16日
    浏览(46)
  • Rust多线程编程

    rust标准库中提供了线程相关支持,直接引用即可使用: 使用spawn方法创建一个线程。如: 创建后线程自动运行: 默认的spawn方法传递不了参数。如果要为线程传递参数,需要使用匿名函数作为spawn的参数,匿名函数也称为闭包。 闭包(匿名函数) 闭包的基本语法: 使用闭包

    2024年02月11日
    浏览(42)
  • rust写一个多线程和协程的例子

    当涉及到多线程和协程时,Rust提供了一些非常强大的工具,其中最常用的库之一是 tokio ,它用于异步编程和协程。下面我将为你展示一个简单的Rust程序,演示如何使用多线程和协程。 首先,你需要在你的项目的 Cargo.toml 文件中添加 tokio 库的依赖: [dependencies] tokio = { versi

    2024年02月11日
    浏览(52)
  • Rust 实现线程安全的 Lock Free 计数器

    完整代码:https://github.com/chiehw/hello_rust/blob/main/crates/counter/src/lib.rs Trait 可以看作是一种 能力的抽象 ,和接口有点类似。Trait 还能作为 泛型约束条件 ,作为参数的限制条件。 使用测试驱动开发可以让目标更明确,这里先写个简单的测试案例。 直接封装 AtomicUsize 使用多线程

    2024年04月13日
    浏览(33)
  • 【rust语言】rust多态实现方式

    学习rust当中遇到了这个问题,记录一下,不对地方望指正 多态是面向对象程序设计中的一个重要概念,指同一个行为或操作在不同实例上具有不同的行为或结果。简单来说,多态就是指同一种类型的对象,在不同的上下文中有不同的行为。多态性使得程序可以更加灵活、可

    2024年02月11日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包