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代码,你需要非常谨慎地确保设计,才能满足线程间的安全性要求。

相关推荐
mldong6 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排6 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60617 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角8 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!8 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰8 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi10 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen10 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马11 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒12 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端