惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
CERT Recently Published Vulnerability Notes
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
WordPress大学
WordPress大学
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
V
Visual Studio Blog
Stack Overflow Blog
Stack Overflow Blog
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Tor Project blog
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Latest news
Latest news
L
LINUX DO - 热门话题
罗磊的独立博客
T
Tenable Blog
The Hacker News
The Hacker News
美团技术团队
N
Netflix TechBlog - Medium
V
Vulnerabilities – Threatpost
阮一峰的网络日志
阮一峰的网络日志
Last Week in AI
Last Week in AI
博客园 - 司徒正美
Jina AI
Jina AI
Cyberwarzone
Cyberwarzone
云风的 BLOG
云风的 BLOG
S
Secure Thoughts
Cloudbric
Cloudbric
S
Security @ Cisco Blogs
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Security Blog
Microsoft Security Blog
Spread Privacy
Spread Privacy
U
Unit 42
雷峰网
雷峰网
C
CXSECURITY Database RSS Feed - CXSecurity.com
Webroot Blog
Webroot Blog
爱范儿
爱范儿
博客园 - 【当耐特】
Know Your Adversary
Know Your Adversary
P
Privacy International News Feed
P
Palo Alto Networks Blog
Google Online Security Blog
Google Online Security Blog
The Last Watchdog
The Last Watchdog
博客园 - 聂微东
Help Net Security
Help Net Security
Hacker News: Ask HN
Hacker News: Ask HN
F
Full Disclosure
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Security Affairs
Project Zero
Project Zero

博客园 - PKICA

博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 博文阅读密码验证 - 博客园 rust线程-std::thread::park和unpark 配合Builder实现轻量级的线程挂起与唤醒 rust自定义线程属性std::thread::Builder rust线程 rust关联函数 rust高并发设计实践进阶 rust高并发设计实践 rust性能优化与安全边界 联合体union在rust和C语言中有什么区别 如何预防rust不良的代码设计总结 rust类型系统与零成本抽象 rust内存模型 告别大显存依赖!用 Rust 新一代深度学习框架 Burn 打造纯 CPU 文本分类推理引擎 rust类型系统标记 编译配置解答 git实用命令 rust底层设计理念值得注意的几个地方总结 rust可变引用作为函数参数的机理详解 Rust内存重解释transmute C与Rust类型映射 Rust FFI 安全抽象范式 rust延迟初始化原语 rust重借用机制与原理 rust参数传递模型 汇编语言语法详解 gdb汇编调试 gdb-pwndbg的安装与使用指南 gdb调试插件gef C语言thread_local linux系统readelf命令使用指南 gcore转储进程内存 gdb查看命令 RGB与YUV颜色编码的区别 Rust原子类型 C++ STL求两个集合交集差集 gdb调试集锦 ubuntu24.0.4使用root用户登录 ubuntu24.0.4输入密码后跳回登录界面 AI内存压缩技术TurboQuant及存疑 ubuntu切换到指定内核版本 在没有顶级科技大佬直接背书的情况下deepseek为啥能够异军突起? HuggingFace和deepseek的关系 当前主流AI大模型 Rust写时克隆Cow系列2
rust并发与异步编程
PKICA · 2026-07-22 · via 博客园 - PKICA

无论是c/c++,还是rust开发,都无可避免地需要进行多并发与异步编程,接下来以问题的方式进行讲解。

问题一:什么是 Mutex 的锁“毒化(Poisoning)”机制?它与展开安全(Unwind Safety)有何关系?

1. 定义

  • 锁毒化(Lock Poisoning):在 Rust 标准库中,当一个线程在持有 MutexGuard(即锁)期间发生了 Panic(恐慌),Rust 会在线程栈展开时,自动将该 Mutex 标记为 Poisoned(已毒化) 状态。
  • 状态损坏保护:毒化机制的核心目的是为了保护不变量(Invariants)。当一个线程修改临界区数据中途发生 Panic 时,数据极有可能处于不一致或损坏的中间状态。

2. 底层行为与设计哲学

  • 后续其他线程再调用 Mutex::lock() 时,不会直接成功,而是返回一个 Err(PoisonError)
  • 这一设计完美契合了 Rust 的 展开安全(Unwind Safety) 概念:通过在编译期和运行期设置屏障,防止程序在捕获 Panic(如使用 catch_unwind)或跨线程通信时,意外读取到已经被破坏的、处于非安全状态的数据。
  • 如何恢复:如果你确信可以安全忽略损坏,或者可以通过重置不变量来修复它,可以通过 let mut data = rust_mutex.lock().unwrap_or_else(|e| e.into_inner()); 强行获取内部数据。

❌ 代码反例

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

fn main() {
    // 一个维持着“总和等于10”这一不变量的结构
    let mutex = std::sync::Arc::new(Mutex::new(vec![5, 5]));

    let c_mutex = mutex.clone();
    let handle = thread::spawn(move || {
        let mut data = c_mutex.lock().unwrap();
        data[0] = 10; // 不变量已被打破(此时总和为15)
        panic!("Thread panicked while holding the lock!"); // 显式触发 Panic,引发栈展开
    });

    let _ = handle.join(); // 捕获子线程崩溃

    // ❌ 另一个线程尝试获取锁,会返回 Err(PoisonError)
    match mutex.lock() {
        Ok(_) => println!("Lock acquired successfully."),
        Err(poison_err) => {
            // 术语:PoisonError
            println!("Error: Mutex is poisoned! Inner data: {:?}", poison_err.into_inner());
        }
    }
}

问题二:什么是异步任务的取消安全性(Cancellation Safety)?当 tokio::select! 分支被放弃时会发生什么?

1. 术语定义

  • 取消安全性(Cancellation Safety):简单理解为原子性异步,直白理解为“当这个任务被外部框架强制取消(Drop)时,整个系统是否还能保持安全。”。在 Rust 异步生态(如 Tokio)中,如果一个 Future 在被丢弃(Dropped)时,即使该 Future 还没有运行到 Poll::Ready 状态,它也不会丢失任何正在处理的数据或破坏底层资源状态,那么这个异步操作就是取消安全的(Cancellation-safe)。在linux c编程中,有一个可重入函数(同一个函数在被多次重叠调用(Overlapping Invocation)时是否安全。),

    “可重入”描述的是函数/代码(Code)的属性;而 Rust 异步的核心是数据/状态机(State)。引发灾难的不是代码被并发执行,而是由于引用的数据(Future 实例)突然提前解构,导致状态不完整

  • 显式丢弃(Drop-based Cancellation):Rust 的异步任务取消是通过直接销毁 Future 实例实现的(即调用其 Drop 析构函数)。

2. tokio::select! 的放弃机制与隐患

  • tokio::select! 宏会同时并发轮询(Poll)多个 Future。一旦其中一个分支率先返回 Poll::Readyselect! 会立即丢弃(Drop)其他所有未完成的分支。
  • 如果被丢弃的分支所对应的 Future非取消安全的(Cancellation-unsafe),那么它在过去几次被 poll 期间暂存在异步状态机内部(栈帧上)的数据和进度就会彻底蒸发。

❌ 代码反例(非取消安全的异步操作)

use tokio::net::TcpStream;
use tokio::io::AsyncReadExt;

async fn cancel_unsafe_demo(stream: &mut TcpStream) {
    let mut buffer = [0u8; 1024];
    
    tokio::select! {
        // ❌ tokio::io::AsyncReadExt::read 是非取消安全的
        // 如果这里读取了半包数据(例如4个字节),接着下面的超时分支触发了
        res = stream.read(&mut buffer) => {
            println!("Read {} bytes", res.unwrap());
        }
        _ = tokio::time::sleep(tokio::time::Duration::from_millis(10)) => {
            // 超时触发,上面的 `stream.read` Future 会被直接 Drop。
            // 此时,这 4 个字节的数据已经从操作系统的 TCP 缓冲区移走,但由于 Future 被销毁,
            // 这部分数据彻底丢失在虚空中,后续再次读取将导致数据流损坏!
            println!("Timeout triggered, read branch dropped!");
        }
    }
}

问题三:为什么自引用结构体(Self-referential Struct)是不安全的?Pin 又是如何解决它的?

1. 术语定义

  • 自引用结构体(Self-referential Struct):指结构体内部的某个字段(指针/引用),指向了该结构体自身的另一个字段。
  • 移动语义(Move Semantics)与内存损坏:Rust 的一切类型默认都是可以被在内存中移动的(Move)。当自引用结构体发生位移动(例如作为返回值返回、放入 Vec 中)时,原字段的内存地址发生了改变,但内部的指针字段依然指向旧的、已经失效的内存地址(即悬空指针,Dangling Pointer),这直接违反了内存安全。

2. PinUnpin 的救赎

  • 固定位置(Pinned Place):Pin<P> 是一个指针包装器。它的核心作用是将一个指针所指向的数据“钉”在内存的绝对位置上,确保该数据在生命周期结束前,绝不会再发生任何内存移动(Move)。
  • 自动特质 Unpin(Auto Trait):绝大多数类型都默认实现了 Unpin,代表它们移动时很安全,不受 Pin 的约束。而异步生成器/编译后产生的 Future 状态机(内部由于暂存局部引用,本质就是自引用结构体)则显式地去除了 Unpin(即 impl !Unpin)。
  • 通过 Pin<&mut Self>,Rust 编译器向异步运行时保证:这个 Future 的状态机在后续的轮询(Poll)过程中绝对不会被移动,从而允许在不安全的自引用结构上安全地构建高层异步流。

❌ 代码反例(模拟 Future 底层的自引用损坏)

use std::marker::PhantomPinned;
use std::pin::Pin;

// 术语:自引用结构体(包含 !Unpin 的非安全类型)
struct SelfRef {
    value: String,
    // 故意让内部指针指向自己的 value 字段
    pointer_to_value: *const String, 
    _marker: PhantomPinned, // 术语:去除了 Unpin 特质(即 !Unpin),标记该类型不可移动
}

fn main() {
    // 在栈上创建一个自引用结构
    let mut obj = SelfRef {
        value: String::from("Hello"),
        pointer_to_value: std::ptr::null(),
        _marker: PhantomPinned,
    };
    obj.pointer_to_value = &obj.value; // 让指针指向自身

    // ❌ 错误做法:没有使用 Pin,直接在栈上发生了值移动(Move)
    let moved_obj = obj; 

    // 术语:未定义行为(Undefined Behavior)
    // 此时 moved_obj.pointer_to_value 依然指向原先 `obj` 的旧栈地址,
    // 而旧地址已被标记为无效,访问它将导致灾难性的内存损坏!
    unsafe {
        println!("Old Address Pointer: {:?}", *moved_obj.pointer_to_value);
    }
}

参考资料:

1.rust语言关键字move