[Rust] Ref, Arc, Rc, Mutex

고승우·2023년 8월 27일
0

Rust

목록 보기
5/16

Arc, Rc, Refcell, 그리고 Mutex는 ownership과 borrow가 가진 문제를 해결할 수 있도록 도와준다.

Ref

동일한 데이터에 대한 여러 reference를 동시에 생성하고 사용할 수 있는 스마트 포인터. 단일 thread 내에서 데이터에 대한 shared reference를 관리하는 데 사용되고 std::cell::RefCell을 사용하여 구현된다. RefCell은 런타임 borrowing 규칙을 사용하여 데이터가 unsafe한 방식으로 동시에 접근할 수 없도록 한다.

use std::cell::RefCell;

fn main(){
    let a = RefCell::new(5);
    let b = &a;
    let c = &a;

    *b.borrow_mut() += 1;
    assert_eq!(*c.borrow(), 6);	// OK
}
use std::cell::RefCell;

fn main(){
    let a = RefCell::new(5);
    let b = &a;
    
    let mut c = b.borrow_mut();
    *c = 10;
    let d = a.borrow();
    
    assert_eq!(*d, 10); 	// main' panicked at 'already mutably borrowed: BorrowError'
}

Arc (Atomic Reference Count)

multi thread에서 thread safe 방식으로 데이터를 공유할 수 있는 데이터 유형 (immutable reference)이다. Ref처럼 동일한 데이터에 대한 여러 thread의 reference를 허용하지만, reference count가 안전하게 업데이트 되도록 원자(atomic) 연산을 사용한다.

atomic operation:

  • 중단되지 않고 단일 단위의 작업으로 수행되는 연산
  • Arc는 atomic reference counting을 제공하며, 이는 하나의 원자 단계에서 reference count를 수행하여 thread-safe 방식으로 공유 데이터의 lifetime을 관리하는 데 사용될 수 있음을 의미한다.
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use std::thread;

fn main(){
    let data = Arc::new(Mutex::new(RefCell::new(vec![1, 2, 3])));
    let mut handles = vec![];

    for _ in 0..3 {
        let data = data.clone();
        let handle = thread::spawn(move || {
            let data_locked = data.lock().unwrap();
            let mut data_ref_mut = data_locked.borrow_mut();
            data_ref_mut.push(4);
        });
        handles.push(handle);
    }
    for handle in handles{
        handle.join().unwrap();
    }

    let data = data.lock().unwrap();
    println!("Result {:?}", data);  // Result RefCell { value: [1, 2, 3, 4, 4, 4] }
}

vec에 접근하기 전에 lock을 걸어 thread-safe 액세스를 제공하고, RefCell을 사용하여 mutable하게 빌려와 4를 push한다. 이러한 방식으로 RefCell을 통해 데이터를 수정하고 Mutex를 통해 모두 single thread 컨텍스트에서 읽을 수 있다.

Arc(Refcell(Mutex))

  • thread 간에 데이터를 공유할 수 있다.
  • Arc는 단일 thread 내에서 mutable borrow를 허용하지 않아서 단일 thread 내에서 mutability를 허용하기 위해 RefCell을 사용할 수 있다.
  • 여러 thread가 동시에 데이터를 borrow하고 mutate할 수 있도록 하기 위해 Mutex를 사용하여 데이터에 대한 액세스를 동기화한다.

Rc(reference counted)

Rc<T>는 동일한 데이터에 대한 여러 참조를 가질 수 있는 스마트 포인터 유형이다. reference count가 0에 도달하면 데이터가 삭제된다. Rc는 소유권을 가지지 않고 reference를 여러 개 만들고 싶을 때 유용하다. RefCell는 데이터에 대한 여러 참조를 허용하지만 데이터에 대한 여러 변경 가능한 참조를 허용하지 않기 때문에 RefCell와 결합할 때 특히 유용하다. RefCell을 Rc로 감싸면 데이터에 대한 참조를 가질 수 있고, 변환할 수도 있다. clone()을 활용해 변수를 선언하면, count가 증가한다.

use std::cell::RefCell;
use std::rc::Rc;

struct MyStruct{
    data: i32,
}

fn main(){

    let my_struct = Rc::new(RefCell::new(MyStruct{data: 3}));
    let my_struct_ref1 = my_struct.clone();
    let my_struct_ref2 = my_struct.clone();
    
    my_struct_ref1.borrow_mut().data = 10;
    my_struct_ref2.borrow_mut().data = 20;

    println!("{:?}", my_struct_ref1.borrow().data);  // 20
    println!("{:?}", my_struct_ref2.borrow().data);  // 20
}

Summary

링크에 자세하고 명료하게 잘 정리 되어 있다. 참고하면 좋을 것 같다.

  • shared ownership: Arc and Rc
    - Arc만 비싼 대신 thread-safe
  • mutate something shared: Cell, RefCell, Mutex
    - Mutex만 thread-safe
    • Copy type -> Cell, non Copy type -> RefCell
profile
٩( ᐛ )و 

0개의 댓글