0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Rust的多线程编程概念和使用方法

科技绿洲 来源:TinyZ 作者:TinyZ 2023-09-20 11:15 次阅读

Rust是一种强类型、高性能的系统编程语言,其官方文档中强调了Rust的标准库具有良好的并发编程支持。Thread是Rust中的一种并发编程方式,本文将介绍Rust中thread的相关概念、方法和字段、常见用法以及多线程的一些实践经验。由浅入深带你零基础玩转Rust的多线程编程。

线程的基本概念和使用方法

Thread是Rust中并发编程的一种基本方式。Rust中的Thread使用标准库中的std::thread::Thread结构体表示。我们可以通过下面的代码来创建一个Thread:

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        // 子线程执行的代码
    });
}

其中的||表示闭包,该闭包中的代码将在子线程中执行。调用thread::spawn方法会返回一个Result,该Result包含一个智能指针,该智能指针拥有对线程的所有权,如果线程执行成功则返回Ok,否则返回Err。通过这个智能指针我们可以管理线程的生命周期和操作线程。

当线程中的代码执行完毕时,我们可以使用以下代码将线程加入主线程:

handle.join().expect("执行失败");

Thread也支持通过std::thread::Builder结构体进行创建,Builder提供了一些线程的配置项,如线程名字、线程优先级、栈大小等。

use std::thread;

fn main() {
    let builder = thread::Builder::new().name("my_thread".into());
    let handle = builder.spawn(|| {
        // 子线程执行的代码
    });
}

线程的字段和方法

Thread结构体中提供了一些有用的字段和方法。

线程名称

Rust中的Thread对象有一个名称属性,可以通过thread::current()函数获取当前线程的名称,也可以通过std::thread::Builder结构体设置线程的名称。

use std::thread;

fn main() {
    let thr0 = thread::current();
    let thread_name = thr0.name().unwrap_or("unknown");
    println!("当前线程的名称:{}", thread_name);
    
    let builder = thread::Builder::new().name("my_thread".into());
    let handle = builder.spawn(move || {
        let thr = thread::current();
        let name = thr.name().unwrap_or("unknown");
        println!("当前线程的名称:{}", name);
    });
    handle.expect("执行失败").join().unwrap();
}
//  输出结果:
// 当前线程的名称:main
// 当前线程的名称:my_thread

线程id

Rust中的Thread对象还有一个id属性,可以通过thread::current()函数获取当前线程的id,也可以通过std::thread::Builder结构体设置线程的id。

use std::thread;

fn main() {
    let thread_id = thread::current().id();
    println!("当前线程的id:{:?}", thread_id);
    
    let builder = thread::Builder::new().name("my_thread".into());
    let handle = builder.spawn(|| {
        let id = thread::current().id();
        println!("当前线程的id:{:?}", id);
    });
    handle.expect("执行失败").join().unwrap();
}
//  输出结果:
// 当前线程的id:ThreadId(1)
// 当前线程的id:ThreadId(2)

线程休眠

Rust中Thread对象提供了一个sleep方法,用于让线程休眠指定时间。

use std::{thread, time};

fn main() {
    println!("线程休眠前:{:?}", time::Instant::now());
    thread::sleep(time::Duration::from_secs(2));
    println!("线程休眠后:{:?}", time::Instant::now());
}
//  输出结果:
// 线程休眠前:Instant { tv_sec: 9667960, tv_nsec: 471430161 }
// 线程休眠后:Instant { tv_sec: 9667962, tv_nsec: 471515229 }

线程状态

Rust中Thread对象表示的是系统中的一个线程,可以通过thread::JoinHandle结构体的is_finalized()和thread::Thread的panicking()方法来查看线程是否结束和是否因panic而结束。

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        // TODO: 执行耗费时间的任务
    });
    while !handle.is_finished() {
        thread::sleep_ms(100);
    }
    if thread::panicking() {
        println!("线程因panic而结束");
    } else {
        println!("线程正常结束");
    }
}

常用用法和示例

单线程执行

我们可以使用Thread开启一个单线程,并在该线程中执行我们的代码。当该线程执行完毕后,我们通过JoinHandle.join()方法将该线程加入主线程。

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        println!("Hello Thread!");
    });
    
    handle.join().unwrap();
}

多线程执行

我们可以使用多个Thread对象并行地执行任务,实现多线程编程。

use std::thread;

fn main() {
    let handle1 = thread::spawn(|| {
        for i in 0..5 {
            println!("Thread1: {}", i);
        }
    });
    let handle2 = thread::spawn(|| {
        for i in 0..5 {
            println!("Thread2: {}", i);
        }
    });
    
    handle1.join().unwrap();
    handle2.join().unwrap();
}

线程间通信

Rust中线程间通信可以通过channel实现。在以下例子中,我们开启两个线程,一个线程向channel发送数据,另一个线程从channel接收数据。两个线程可以通过channel实现数据共享和交换。

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

fn main() {
    let (tx, rx) = mpsc::channel();
    let handle1 = thread::spawn(move || {
        tx.send("Hello Thread!".to_string()).unwrap();
    });
    let handle2 = thread::spawn(move || {
        let msg = rx.recv().unwrap();
        println!("{}", msg);
    });
    
    handle1.join().unwrap();
    handle2.join().unwrap();
}

进阶用法:多线程协作和锁

多线程协作

当线程之间需要协作执行任务时,我们可以通过Rust中提供的互斥锁Mutex和读写锁RwLock来实现。

以下是一个简单的例子,在这个例子中我们开启两个线程,一个线程向共享变量加1,另一个线程向共享变量减1。由于有两个线程同时修改共享变量,我们需要使用Mutex来进行加锁和解锁操作。

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

fn main() {
    let shared_count = Arc::new(Mutex::new(0));
    let thread1 = shared_count.clone();
    let handle1 = thread::spawn(move || {
        for _ in 0..10 {
            let mut count = thread1.lock().unwrap();
            *count += 1;
        }
    });
    let thread2 = shared_count.clone();
    let handle2 = thread::spawn(move || {
        for _ in 0..10 {
            let mut count = thread2.lock().unwrap();
            *count -= 1;
        }
    });
    
    handle1.join().unwrap();
    handle2.join().unwrap();
    println!("shared_count: {:?}", *shared_count.lock().unwrap());
}
//    输出结果:
//  shared_count: 0

在多线程编程中,锁是一种常见的同步机制,它用于保护共享数据不受到并发访问的影响。Rust标准库中提供了锁的实现Mutex、RwLock、Barrier、Condvar等等。

Mutex

Mutex是Rust中最基本的锁机制,它提供了互斥访问的机制。当多个线程同时对一个共享资源进行访问时,Mutex会对该资源进行加锁,当一个线程访问该资源时,其他线程无法访问该资源,直到该线程解锁该资源。

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

fn main() {
    let shared_data = Arc::new(Mutex::new(0));
    let thread1 = shared_data.clone();
    let handle1 = thread::spawn(move || {
        for _ in 0..10 {
            let mut data = thread1.lock().unwrap();
            *data += 1;
        }
    });
    let thread2 = shared_data.clone();
    let handle2 = thread::spawn(move || {
        for _ in 0..10 {
            let mut data = thread2.lock().unwrap();
            *data -= 1;
        }
    });
    
    handle1.join().unwrap();
    handle2.join().unwrap();
    println!("shared_data: {:?}", *shared_data.lock().unwrap());
}
//    输出结果:
//  shared_data: 0

RwLock

RwLock是一种读写锁,它提供了两种访问方式:读取访问和写入访问,当同时有多个读操作时,RwLock会共享锁,允许多个线程同时访问该数据,当进行写操作时,RwLock会对该数据进行排它锁,只允许一个线程进行访问。

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

fn main() {
    let shared_data = Arc::new(RwLock::new(0));
    let thread1 = shared_data.clone();
    let handle1 = thread::spawn(move || {
        for _ in 0..10 {
            let mut data = thread1.write().unwrap();
            *data += 1;
        }
    });
    let thread2 = shared_data.clone();
    let handle2 = thread::spawn(move || {
        for _ in 0..10 {
            let data = thread2.read().unwrap();
            println!("data: {:?}", *data);
        }
    });
    
    handle1.join().unwrap();
    handle2.join().unwrap();
    println!("shared_data: {:?}", *shared_data.read().unwrap());
}
//    输出结果:
// data: 10
// data: 10
// data: 10
// data: 10
// data: 10
// data: 10
// data: 10
// data: 10
// data: 10
// data: 10
// shared_data: 10

RwLock还提供了一个try_read()方法,可以进行非阻塞式的读操作。

Barrier

Barrier是一种同步机制,它提供了一个点,当多个线程只有在该点处到达才能继续执行。Barrier有一个计数器,当计数器到达值N时,所有在该Barrier处等待的线程可以继续执行。

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

fn main() {
    let barrier = Arc::new(Barrier::new(3));
    let thread1 = barrier.clone();
    let handle1 = thread::spawn(move || {
        println!("Thread1 step1.");
        thread1.wait();
        println!("Thread1 step2.");
    });
    let thread2 = barrier.clone();
    let handle2 = thread::spawn(move || {
        println!("Thread2 step1.");
        thread2.wait();
        println!("Thread2 step2.");
    });
    
    handle1.join().unwrap();
    handle2.join().unwrap();
}
//    输出结果:
// Thread1 step1.
// Thread2 step1.
// ERROR Timeout

最佳实践:安全地使用Thread

在使用Thread进行多线程编程时,为了保证线程安全,我们需要注意以下几点:

  • • 在多线程程序中避免使用静态变量,单例模式和全局变量,这些变量可能被多个线程同时访问。
  • • 在多线程编程中,一定要避免使用裸指针和内存共享,这种方式可能导致数据竞争和未定义行为。
  • • 使用Rust的锁机制Mutex和RwLock等,保证共享数据的线程安全性。
  • • 编写多线程程序时,应该考虑线程池的设计,防止创建过多的线程带来的资源错乱和性能损失。
  • • 多线程程序的并发度一定要注意控制,过高的并发度反而会导致性能下降。

以上都是在使用Thread时应该注意的一些安全问题,遵循这些原则可以提高多线程程序的可维护性和安全性。

总结

本章节通过代码示例深入的探讨了Rust中thread的线程的基本概念,线程的字段和方法,常用用法和示例,多线程协作和锁以及thread最佳实践经验。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4708

    浏览量

    68175
  • 多线程编程
    +关注

    关注

    0

    文章

    16

    浏览量

    6679
  • Thread
    +关注

    关注

    2

    文章

    83

    浏览量

    25883
  • Rust
    +关注

    关注

    1

    文章

    228

    浏览量

    6538
收藏 人收藏

    评论

    相关推荐

    Python多线程编程原理

    多线程使用方法Python中使用线程有两种方式,分别是函数或者用类来包装线程对象。用函数来包装线程对象的方式用函数来包装
    发表于 11-22 14:01

    多线程解决思路一

    使用方法节点实现多线程,两个线程之间的数据传输也都使用方法节点的方式实现。1、初始化时打开另一个线程。2、程序运行过程中实现对被调
    发表于 07-06 17:21

    嵌入式Linux多线程编程

    嵌入式Linux多线程编程-学习资源-华清远见清远见嵌入式学院:清远见嵌入式学院:《嵌入式应用程序设计》——第5 章 嵌入式Linux 多线程编程第5 章 嵌入式Linux
    发表于 11-05 06:54

    C++面向对象多线程编程 (pdf电子版)

    C++面向对象多线程编程共分13章,全面讲解构建多线程架构与增量多线程编程技术。第1章介绍了
    发表于 09-25 09:39 0次下载

    QNX环境下多线程编程

    介绍了QNX 实时操作系统和多线程编程技术,包括线程间同步的方法多线程程序的分析步骤、线程基本
    发表于 08-12 17:37 30次下载

    linux多线程编程开发

    本文中我们针对 Linux 上多线程编程的主要特性总结出 5 条经验,用以改善 Linux 多线程编程的习惯和避免其中的开发陷阱。在本文中,我们穿插一些 Windows 的
    发表于 12-26 14:24 55次下载
    linux<b class='flag-5'>多线程</b><b class='flag-5'>编程</b>开发

    MFC下的多线程编程

    计算机上的上位机制作工具语言之MFC下的多线程编程
    发表于 09-01 14:55 0次下载

    VC-MFC多线程编程详解

    VC编程中关于 MFC多线程编程的详解文档
    发表于 09-01 15:01 0次下载

    Windows多线程编程

    计算机上的上位机制作工具语言之Windows多线程编程,感兴趣的可以看看。
    发表于 09-01 15:27 0次下载

    关于多线程编程教程及经典应用案例的汇总分析

    在一个程序中,这些独立运行的程序片段叫作线程,利用它编程概念就叫作多线程处理。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一
    发表于 10-16 16:46 0次下载

    linux多线程编程技术

    1 引言 线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。传统的 Unix也支持线程概念,但是在一个进
    发表于 10-24 16:01 5次下载

    什么是多线程编程?多线程编程基础知识

    摘要:多线程编程是现代软件技术中很重要的一个环节。要弄懂多线程,这就要牵涉到多进程。本文主要以多线程编程以及
    发表于 12-08 16:30 1.2w次阅读

    多线程编程指南的PDF电子书免费下载

    多线程编程指南》介绍了 SolarisTM 操作系统 (Solaris Operating System, Solaris OS)中 POSIX®线程和 Solaris 线程
    发表于 06-11 08:00 4次下载
    <b class='flag-5'>多线程</b><b class='flag-5'>编程</b>指南的PDF电子书免费下载

    多线程如何保证数据的同步

    多线程编程是一种并发编程方法,意味着程序中同时运行多个线程,每个线程可独立执行不同的任务,共享
    的头像 发表于 11-17 14:22 1075次阅读

    mfc多线程编程实例

    (图形用户界面)应用程序的开发。在这篇文章中,我们将重点介绍MFC中的多线程编程多线程编程在软件开发中非常重要,它可以实现程序的并发执行,提高程序的效率和响应速度。MFC提供了丰富
    的头像 发表于 12-01 14:29 1351次阅读