多线程下Unsafe Rust操作Vec指针崩溃问题求助
多线程下Unsafe Rust操作Vec指针的崩溃问题
我正在学习理解Unsafe Rust在线程执行中的行为,编写了如下代码:
use std::sync::Arc; use std::sync::atomic::{AtomicPtr, Ordering}; use std::thread; fn main() { let mut arr = vec![1,2,3]; let ptr = &mut arr as *mut Vec<i32>; println!("ptr : {:?}",ptr); // unsafe { (*ptr)[0] = 2; } let ptr_ref_1 = Arc::new(AtomicPtr::new(ptr)); let a = thread::spawn(move || unsafe { let ptr_inner = (ptr_ref_1).load(Ordering::SeqCst); println!("ptr_inner_1 {:?}",ptr_inner); (*ptr_inner).push(4); println!("4 is done"); }); let ptr_ref_2 = Arc::new(AtomicPtr::new(ptr)); let b = thread::spawn(move || unsafe { let ptr_inner = (ptr_ref_2).load(Ordering::SeqCst); println!("ptr_inner_2 {:?}",ptr_inner); (*ptr_inner).push(5); println!("5 is done"); }); let ptr_ref_3 = Arc::new(AtomicPtr::new(ptr)); let c = thread::spawn(move || unsafe { // Read value // println!("{:?}",*(ptr_ref_3.load(Ordering::SeqCst))); let ptr_inner = (ptr_ref_3).load(Ordering::SeqCst); println!("ptr_inner_3 {:?}",ptr_inner); (*ptr_inner).push(6); println!("6 is done"); }); a.join().unwrap(); b.join().unwrap(); c.join().unwrap(); println!("final values {:?}",arr); }
这段代码尝试通过多个线程使用Unsafe Rust和AtomicPtr操作同一个Vec指针,95%的运行情况正常,但有5%的运行会抛出如下错误:
Output : ptr : 0x16d65f008 ptr_inner_1 0x16d65f008 ptr_inner_2 0x16d65f008 5 is done main(75339,0x16d867000) malloc: pointer 0x6000022a8010 being reallocated was not allocated main(75339,0x16d867000) malloc: *** set a breakpoint in malloc_error_break to debug Signal: SIGABRT (signal SIGABRT)
我了解如何使用Arc/Mutex在多线程中更新值,但不清楚这段Unsafe Rust代码的错误所在,希望有人能解释问题出在哪里。
问题根源
这段代码的核心问题在于多线程无同步地调用Vec::push违反了线程安全规则,触发了内存分配器的冲突:
Vec并非线程安全类型,它内部包含堆内存指针、长度、容量三个字段。当push遇到容量不足时,会执行重分配:释放旧堆内存、分配新内存、拷贝数据,最后更新内部的指针、容量字段。这些操作都不是原子的,也没有任何同步机制。- 当多个线程并发执行
push时,极有可能出现以下致命场景:- 线程A检测到容量不足,启动重分配流程,已经释放了旧堆内存,但还没完成新内存分配和内部字段更新。
- 线程B此时也检测到容量不足,拿着已经被释放的旧堆指针去执行重分配/释放操作,直接触发malloc的错误——操作了一块已经不属于当前进程的内存。
AtomicPtr仅保证了指针本身的原子加载,但完全没有保护Vec内部状态的并发访问,多线程同时修改Vec的内部字段会导致数据竞争,彻底破坏Vec的结构。
为什么大部分时候能正常运行?
这是线程调度随机性导致的:如果三个线程的push操作刚好串行执行,或者重分配没有在并发时触发,就不会出现冲突;但一旦重分配发生在多线程并发执行阶段,内存错误就会触发。
正确的做法
你已经知道的Arc<Mutex<Vec<i32>>>是标准的解决方案:
Mutex保证同一时间只有一个线程能访问内部的Vec,彻底避免了并发修改带来的数据竞争和内存错误。Arc提供了线程间共享所有权的能力,让多个线程能安全持有同一个Vec的引用。
内容的提问来源于stack exchange,提问作者Nikhil Chatragadda
相关产品推荐
相关产品推荐

