Rust多线程编程

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

Rust多线程编程

使用线程模块

rust标准库中提供了线程相关支持,直接引用即可使用:

use std::thread;

创建线程

使用spawn方法创建一个线程。如:

use std::thread;/* 引用线程模块 */
use std::time::Duration;
fn main() {
    std::thread::spawn(thread_function);
    loop {
        thread::sleep(Duration::from_secs(1));/* sleep 1s */
        println!("main thread running..");
    }
}

fn thread_function(){
    loop {
        thread::sleep(Duration::from_secs(1));/* sleep 1s */
        println!("demo thread running..");
    }
}

创建后线程自动运行:

boys@server:~/rust_study/demo$ cargo run
   Compiling demo v0.1.0 (/home/boys/rust_study/demo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/demo`
main thread running..
demo thread running..
main thread running..
demo thread running..
main thread running..
demo thread running..
^C
boys@server:~/rust_study/demo$ 

线程传参

默认的spawn方法传递不了参数。如果要为线程传递参数,需要使用匿名函数作为spawn的参数,匿名函数也称为闭包。

闭包(匿名函数)

闭包的基本语法:

|param1, param2, ...| {
    // 函数体
}

使用闭包的好处是可以捕获函数体外的变量,传递到函数体内。

按捕获变量方式可以分为:

  • 值捕获
  • 普通引用捕获
  • 可变引用捕获
值捕获

值捕获的方式会变量的所有权会转移到闭包内,外部无法再使用。如:

fn main(){
    let str = String::from("hello");
    let closure_print_string = move ||{/* move表明函数体内捕获的变量使用值捕获方式 */
        println!("number = {}", str);/* 使用值捕获方式捕获外部的str */
    };
    closure_print_string();
    println!("test for str: {}", str);/* 值捕获方式,str的所有权已经被转移到闭包内,这里无法再使用 */
}
不可变引用捕获

闭包会自动识别捕获的变量类型。根据函数体内捕获的变量的类型,确认捕获方式。

不可变变量按照不可变引用方式捕获使用,因此无法修改原变量值,只能访问变量值。如:

fn main(){
    let str = String::from("hello");/* str是不可变 */
    let closure = ||{
        println!("str: {}", str);
        // str.push_str("world");/* 不可变的变量按不可变引用方式捕获,此处修改了变量值会报错 */
    };
    closure();
    println!("test for str: {}", str);
}
可变引用捕获

将外部变量变为可变变量,即可在闭包函数体内修改变量的值。

fn main(){
    let mut str = String::from("hello");/* str是不可变 */
    let mut closure = ||{
        println!("before push, str: {}", str);
        str.push_str(" world");/* 不可变的变量按不可变引用方式捕获,此处修改了变量值会报错 */
    };
    closure();
    println!("test for str: {}", str);
}

同时闭包的类型也要定义为mut可变的。只要捕获外部的可变变量,都是定义为mut的。如:

fn main(){
    let num = 123;
    let mut str = String::from("hello");/* str是不可变 */
    let mut closure = ||{
        println!("num: {}", num);/* num不可变 */
        println!("before push, str: {}", str);
        str.push_str(" world");/* 不可变的变量按不可变引用方式捕获,此处修改了变量值会报错 */
    };
    closure();
    println!("test for str: {}", str);
}

线程闭包传参

现在就可以使用闭包的方法,将外部参数传递给线程了。

如:

fn main(){
    let word = "what are words";
    let thread_handle = std::thread::spawn(move ||{
        println!("just print 'word': {}", word);
    });
    thread_handle.join().unwrap();
}

这里必须使用值捕获方式,将字符串引用变量str的所有权转移到闭包内。因为编译时会检查闭包和当前函数的声明周期,发现闭包可能会在当前的函数结束后运行,因为闭包传递到了另一个线程。

closure may outlive the current function, but it borrows `word`, which is owned by the current function

更优雅地传参

通过闭包虽然可以传递外部参数到另一个线程,但闭包一般都用来实现比较简单的功能。对于线程来说,会有比较复杂的功能,所以更优雅的方式还是使用函数对一个线程做封装后,再闭包传递参数创建线程。如:

use std::time::Duration;
struct ThreadParam{
    thread_name: String
}
fn demo_thread(param: ThreadParam){
    for _ in 0..3{/* 使用下划线作为迭代变量,避免编译警告 */
        std::thread::sleep(Duration::from_secs(1));
        println!("thread's name: {}", param.thread_name);
    }
}
fn main(){
    let thread_arguments = ThreadParam{
        thread_name: String::from("demo_thread")
    };
    let thread_handle = std::thread::spawn(move ||{
        demo_thread(thread_arguments);
    });
    thread_handle.join().unwrap();/* 回收线程,防止主线程退出看不到线程效果 */
}

回收线程

使用join方法回收线程资源。如:

use std::thread;/* 引用线程模块 */
use std::time::Duration;
fn main() {
    let thread_handle = std::thread::spawn(thread_function);
    thread_handle.join().unwrap();
    println!("thread exit.");
}

fn thread_function(){
    let mut count = 1;
    loop {
        thread::sleep(Duration::from_secs(1));/* sleep 1s */
        println!("demo thread run {} time..", count);
        count = count + 1;
        if count > 3 {
            break;
        }
    }
}
boys@server:~/rust_study/demo$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/demo`
demo thread run 1 time..
demo thread run 2 time..
demo thread run 3 time..
thread exit.

线程同步和通信

rust线程同步和通信提供了几种自带的机制:

  • channel(通道)
  • Mutex(互斥锁)
  • 原子类型(Atomic Types)
  • 条件变量(Condition Variable)
  • 原子引用计数(Arc)
  • 栅栏(Barrier)

channel 通道

通道只能使用在多生产者、单消费者的场景下,所在模块是 std::sync::mpsc,mpsc是"Multi-Producer, Single-Consumer" 的缩写,即多生产者单消费者。

use std::sync::mpsc;

使用示例:

fn main(){
    let (sender, receiver) = std::sync::mpsc::channel();
    let sender1 = sender.clone();/* 通过克隆2个sender传递到线程中 */
    let sender2 = sender.clone();

    let sender1_thread_handle = std::thread::spawn(move||{
        std::thread::sleep(std::time::Duration::from_secs(1));
        sender1.send("hello").unwrap();
    });
    let sender2_thread_handle = std::thread::spawn(move||{
        std::thread::sleep(std::time::Duration::from_secs(2));
        sender2.send("bye").unwrap();
    });
    let mut is_connected = false;
    loop {
        let recv_data = receiver.recv().unwrap();
        if recv_data == "hello"{
            println!("recv 'hello', connected");
            is_connected = true;
        }
        if is_connected == true{
            if recv_data == "bye"{
                println!("recv 'bye', disconnected");
                break;
            }
        }
    }
    sender1_thread_handle.join().unwrap();
    sender2_thread_handle.join().unwrap();
}

mutex 互斥锁

导入模块:

use std::sync::Mutex;

Rust 中的互斥锁(Mutex)是一个泛型类型。它被定义为 std::sync::Mutex<T>,其中的 T 是要保护的共享数据的类型。初始化时会自动判断数据类型。

下面是使用Mutex对整形变量做互斥访问的示例:

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

fn main() {
    let data = Arc::new(Mutex::new(42));

    let thread1_data = Arc::clone(&data);
    let handle1 = thread::spawn(move || {
        // 使用 thread1_data 进行操作
        let mut value = thread1_data.lock().unwrap();
        *value += 1;
        println!("thread1 Value: {}", *value);
    });

    let thread2_data = Arc::clone(&data);
    let handle2 = thread::spawn(move || {
        // 使用 thread2_data 进行操作
        let value = thread2_data.lock().unwrap();
        println!("thread2 Value: {}", *value);
    });

    handle1.join().unwrap();
    handle2.join().unwrap();
}

这里使用了Arc原子引用计数类型,实现多个线程共享互斥锁的所有权。

Barrier 栅栏

在 Rust 中,你可以使用 Barrier 类型来实现线程栅栏。 Barrier 允许一组线程都到达一个点后再同时继续执行。

示例:

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

fn main() {
    let barrier = Arc::new(Barrier::new(3)); // 创建一个包含3个参与者的栅栏

    for i in 0..3 {/* 创建3个线程 */
        /* 使用Arc原子引用计数将barrier共享给所有线程 */
        let barrier_clone = Arc::clone(&barrier);

        thread::spawn(move || {
            println!("Thread {} before barrier", i);
        
            barrier_clone.wait(); // 所有线程都到达此处后会同时继续执行
        
            println!("Thread {} after barrier", i);
        });
    }

    thread::sleep(std::time::Duration::from_secs(2)); // 等待足够的时间以确保所有线程完成
  
    println!("Main thread");
}

Atomic Types 原子类型

Rust中常用的原子类型有:

  1. AtomicBool 原子布尔类型
  2. AtomicI32 原子整数类型
  3. AtomicPtr 原子指针类型

原子类型是不可分割的类型,对原子类型变量的操作不可分割,因此,常用在多线程并发场景,避免出现竞态问题。

简单的使用示例:

use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::Arc;
use std::thread;

fn main() {
    let atomic_counter = Arc::new(AtomicI32::new(0));
    let mut handles = vec![];/* 使用vec保存线程句柄,回收资源使用 */

    for _ in 0..5 {
        let counter = Arc::clone(&atomic_counter);
        let handle = thread::spawn(move || {
            for _ in 0..1_0 {
                counter.fetch_add(1, Ordering::SeqCst);
            }
        });
        handles.push(handle);
    }

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

    println!("Final value: {}", atomic_counter.load(Ordering::SeqCst));
}

创建了5个线程对原子变量进行递增10次,输出结果为50。若未使用原子变量且不加互斥保护,得到的结果是未知的。文章来源地址https://www.toymoban.com/news/detail-681855.html

boys@server:~/rust_study/demo$ cargo run 
   Compiling demo v0.1.0 (/home/boys/rust_study/demo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/demo`
Final value: 50

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

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

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

相关文章

  • Rust多线程编程

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

    2024年02月11日
    浏览(25)
  • 【跟小嘉学 Rust 编程】十六、无畏并发(Fearless Concurrency)

    【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学 Rust 编程】六、枚举

    2024年02月11日
    浏览(26)
  • Rust编程语言入门之最后的项目:多线程 Web 服务器

    在 socket 上监听 TCP 连接 解析少量的 HTTP 请求 创建一个合适的 HTTP 响应 使用线程池改进服务器的吞吐量 优雅的停机和清理 注意:并不是最佳实践 创建项目 main.rs 文件 修改一: 修改二: 修改三: 修改四: 修改五: hello.html 文件 404.html 文件 单线程Web服务器 开启线程 lib.r

    2023年04月25日
    浏览(44)
  • Rust 是一种面向系统编程语言 主要被设计用来解决执行速度、安全性、并发性和可靠性等方面的问题 Rust From First Principles: Building a Minimal Rust

    作者:禅与计算机程序设计艺术 Rust 是一种面向系统编程语言,主要被设计用来解决执行速度、安全性、并发性和可靠性等方面的问题。相比于其他语言来说,它拥有以下优点: 高性能: Rust 的运行时是单线程的,但是拥有基于垃圾收集(GC)的自动内存管理机制,使得在开

    2024年02月07日
    浏览(45)
  • 【Rust】Rust学习 第十六章无畏并发

    安全且高效的处理并发编程是 Rust 的另一个主要目标。 并发编程( Concurrent programming ),代表程序的不同部分相互独立的执行,而 并行编程( parallel programming )代表程序不同部分于同时执行 ,这两个概念随着计算机越来越多的利用多处理器的优势时显得愈发重要。由于历

    2024年02月12日
    浏览(32)
  • Rust 并发

    thread::spawn() 创建新线程 参数是一个闭包,新线程执行的代码 当主线程结束时,不管其他子线程是否结束,都会结束所有的子线程 可以使用 join()方法,等待子线程执行完 thread::spawn()会返回 joint_handler 从一个线程中,将所有权转移到另一个线程中 channel mpsc ::channel(多个生产者

    2024年01月23日
    浏览(18)
  • 没有synchronized,rust怎么防并发?

    学过Java的同学对synchronized肯定不陌生,那么rust里怎么办呢? 在Rust中,可以使用标准库提供的 std::sync::Mutex 来实现加锁功能。Mutex是互斥锁的一种实现,用于保护共享数据在并发访问时的安全性。 下面是一个简单的示例代码,展示了如何在Rust中使用Mutex进行加锁: 在上述代

    2024年02月13日
    浏览(23)
  • rust实践-异步并发socket通信

    String::from_utf8_lossy() 函数将字节数组转换为字符串 接受一个字节数组作为参数,并尝试将其转换为 UTF-8 编码的字符串 如果字节数组包含无效的 UTF-8 字符,则这些字符将被替换为问号 ? from_utf8_lossy() 函数返回一个 Cow 类型的对象 这个对象实现了 ToString trait 所以可以使用 to_s

    2024年02月13日
    浏览(28)
  • Rust 中 Actor 并发模型的实践与使用

    Actor 模型是一种并行计算模型,提供了一种用于构建并发、分布式系统的形象办法。在 Actor 模型中, 计算被示意为独立的、轻量级的计算单元,称为 Actor ,能够发送和接管音讯并进行本地计算。 作为一种通用的消息传递编程模型,被广泛用于构建大规模可伸缩分布式系统。

    2024年04月28日
    浏览(21)
  • 【跟小嘉学 Rust 编程】一、Rust 编程基础

    【跟小嘉学 Rust 编程】一、Rust 编程基础 本系列旨在分享 Rust 学习心得,适合初学者入门,后续系列会有 Rust 项目实战系列编程介绍。 主要教材参考 《The Rust Programming Language》 Rust 是一门新的编程语言,它可以让每个人编写可靠且高效的程序,使用于需要运行时速度、需要内

    2024年02月10日
    浏览(36)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包