55_使用Sync和Send来扩展并发

1. 概述

rust语言本身的并发特性较少,目前讲的并发特性都是来自标准库(而不是语言本身)。

其实我们无需仅限于标准库的并发,可以自己实现并发。

但在rust语言中有两个并发概念:

  • std::marker::Syncstd::marker::Send这两个trait。

2. Send

Send:允许线程间转移所有权。

  • 实现Send trait的类型可以在线程间转移所有权,Rust中几乎所有的类型都实现了Send
    • Rc<T>没有实现Send,它只用于单线程的场景。
  • 任何完全由Send组成的类型也被标记为Send
  • 实际上除了原始指针之外,几乎所有的基础类型都实现了Send

例如:

rust 复制代码
use std::thread;

fn main() {
    let number = 5;
    let handle = thread::spawn(move || {
        println!("{}", number); // 这里可以正常工作,因为number实现了Send trait
    });
    println!("{}", number);

    handle.join().unwrap();
}

我们创建了一个新的线程,并把一个number变量移动到这个新线程中。这是因为number的类型(在这个例子中是i32)实现了Send trait,所以我们可以安全地在两个线程之间转移所有权的所有权。

3. Sync

Sync:允许从多线程同时访问。

  • 实现Sync的类型可以安全的被多个线程引用
  • 也就是说:如果T实现了Sync,那么&T(T的引用)就是实现了Send
    • 表示T的引用可以被安全地送往另一个线程
  • 基础类型都实现了Sync
  • 完全由Sync类型组成的类型也是Sync
    • 但是Rc<T>不是Sync
    • RefCell<T>Cell<T>家族也不是Sync
    • Mutex<T>实现了Sync
rust 复制代码
use std::sync::Arc;
use std::thread;

fn main() {
    let shared_data = Arc::new(Some(5)); // Arc封装了一个可共享的数据,并且可以安全地在多个线程之间共享。
    let handle = thread::spawn(move || {
        println!("{:?}", shared_data); // 这里可以正常工作,因为Arc<T>实现了Sync trait
    });

    handle.join().unwrap();
}

我们创建了一个新的线程,并尝试在两个线程之间共享一个Arc<T>对象。这是因为Arc<T>(通过其内部的类型T)实现了Sync trait,所以我们可以安全地在多个线程之间共享这个对象。

如果我们尝试在两个线程之间直接共享一个没有实现Sync的对象,Rust编译器会给出错误。

4. 手动实现Send和Sync很难保证安全

手动来实现Send和Sync是很难保证安全的,因为手动实现这些trait的时候涉及到使用特殊不安全的Rust代码,你需要非常谨慎地确保设计,才能满足线程间的安全性要求。

相关推荐
码云之上3 小时前
换模型之后,Chatbot 为什么要自己做 compact?
前端·agent·前端工程化
星栈3 小时前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·agent·weui
研☆香4 小时前
简单的图片上传 删除 预览
前端
ITresearchGuest4 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·人工智能·信息可视化
慧一居士5 小时前
Vite项目中使用Less步骤,详细使用示例
前端·css
花间相见5 小时前
【LangChain组件03】—— LangChain Agents 执行与状态:工作流程与状态管理实战
java·前端·langchain
kyriewen6 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试
计算机魔术师6 小时前
700吉瓦「幽灵用电」:美国AI数据中心的抢电闹剧正在失控
前端
IT_陈寒6 小时前
JavaScript数组排序踩的坑,差点让我加班到凌晨
前端·人工智能·后端
风骏时光牛马6 小时前
提示词工程:大模型交互的核心构建艺术
前端