Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Тема
Описание
Доп.

Atomic Reference Counted (Потокобезопасный указатель атомарного подсчета ссылок)

std::sync::Arc

wrapper-types-in-rust-choosing-your-guarantees

atomics/building-arc

📌 Новый раздел

  • 👾 Best practice
  • Варианты применения

Тип Arc<T> обеспечивает совместное владение значением типа T, размещенным в куче. Вызов Arc::clone создает новый экземпляр Arc, который указывает на то же место в куче, что и источник Arc, при этом увеличивая счетчик ссылок. Когда последний Arc указатель на данное выделение уничтожается, значение, хранящееся в этом выделении (часто называемое «внутренним значением»), также удаляется.

Arc<T> будет реализовывать Send и Sync до тех пор, пока T реализуется Send и Sync

Если вам нужно мутировать через Arc, используйте Mutex, RwLock или один из Atomic типов:

  • Arc<Mutex<RefCell<T>>> - потокобезопасное совместное владение с внутренней изменчивостью и взаимной изоляцией.
  • Arc<RwLock<RefCell<T>>>
  • Arc<AtomicUsize>

crate parking_lot

Замена для Mutex, RwLock, Condvar, and Once меньше, быстрее и более гибким, чем в стандартной библиотеке ржавчины, а также ReentrantMutex тип, который поддерживает рекурсивную блокировку. Он также предоставляет низкоуровневый API для создания ваших собственных эффективных примитивов синхронизации.

Arc реализует traits:

impl<T> Clone for Arc<T>
impl<T> Default for Arc<T>
impl From<&str> for Arc<str>
impl<T> Deref for Arc<T>

Clone:


fn main(){
    let val = Arc::new(AtomicUsize::new(5));
    let val = Arc::clone(&val);
}

Default:


fn main(){
    let x: Arc = Default::default();
    assert_eq!(*x, 0);
}

From<&str>:


fn main(){
    let shared: Arc = Arc::from("eggplant");
}

Defer:


fn main(){
    let val = Arc::new(AtomicUsize::new(5));
    val.fetch_add(1, Ordering::SeqCst); // метод fetch_add через Deref взят у AtomicUsize
}

Defer:


use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main(){
    let val = Arc::new(AtomicUsize::new(5));

    for _ in 0..10 {
        let val = Arc::clone(&val);

        thread::spawn(move || {
            let v = val.fetch_add(1, Ordering::SeqCst);// метод fetch_add через Deref взят у atomic AtomicUsize
            println!("{:?}", v);
        });
    }
}

Методы:

  • downcast() - Попытайтесь привести Arc<dyn Any + Send + Sync> к конкретному типу
  • downcast_unchecked() - Приводит Arc<dyn Any + Send + Sync> к конкретному типу без проверки
  • downgrade()->Weak<T> - Создает новый слабый указатель weak.
  • get_mut()->Option<&mut T> - Возвращает изменяемую ссылку на данные, если нет других указателей
  • get_mut_unchecked()->&mut T Возвращает изменяемую ссылку на данные без проверки.
  • make_mut()->&mut T - Создает изменяемую ссылку на данную Arc при наличии других ссылок создаст clone

  • into_inner()->Option<T> - Расходует и возвращает внутреннее значение, если существует ровно одна strong ссылка.
  • try_unwrap(Arc<T>)->Result<T, Arc<T>> - Возвращает внутреннее значение, если у Arc ровно одна strong ссылка
  • unwrap_or_clone()->T - Возвращает T при единственной ссылке. В противном случае верните клон T

  • strong_count() - количество strong указателей
  • weak_count() - количество weak указателей
  • decrement_strong_count() - Уменьшает счетчик strong ссылок Arc<T> на единицу
  • increment_strong_count() - Увеличивает счетчик strong ссылок Arc<T> на единицу

  • new() - Создает новый Arc<T>
  • new_cyclic() - Создает новый Arc<T> и предоставляя вам Weak<T> ссылающийся на самого себя
  • new_uninit() - Создает Arc с неинициализированным содержимым
  • new_uninit_slice() - Создает Arc slice с атомарным подсчетом ссылок с неинициализированным содержимым.
  • new_zeroed() - Создает Arc с неинициализированным содержимым при этом память заполняется 0 байтами.
  • new_zeroed_slice() - как new_uninit_slice при этом память заполняется 0 байтами.
  • pin() - Создает новый Pin<Arc<T>>

  • ptr_eq() - проверка указывают ли Arc на одно и то же распределение

  • as_ptr()-> *const T - Предоставляет необработанный указатель на данные.
  • from_raw() - Создает Arc<T> из необработанного указателя.
  • into_raw()-> *const T - Потребляет Arc, возвращая завернутый указатель (+using Arc::from_raw)
  • assume_init()

  • try_new() - Создает новый Arc<T>, возвращая ошибку, если выделение не удалось.
  • try_new_uninit()
  • try_new_zeroed()
  • try_pin()

new(data: T) -> Arc<T>


use std::sync::Arc;
fn main(){
    let five = Arc::new(5);
}

downgrade()->Weak<T> - Создает новый слабый указатель weak.

get_mut()->Option<&mut T> - Возвращает изменяемую ссылку на данную Arc, если нет других указателей

downcast() - Попытайтесь привести Arc<dyn Any + Send + Sync> к конкретному типу

Создает новый слабый указатель Weak на это значение


use std::sync::Arc;
fn main(){
    let five = Arc::new(5);
    let weak_five = Arc::downgrade(&five);
}

Не клонирует в отличии от make_mut. Возвращает измененную ссылку на внутреннее значение, если нет других указателей Arc или Weak для одного и того же значения. Возвращает None в противном случае, потому что нецелесообразно изменять общее значение.


use std::sync::Arc;
fn main(){
    let mut x = Arc::new(3);
    *Arc::get_mut(&mut x).unwrap() = 4;
    assert_eq!(*x, 4);

    let _y = Arc::clone(&x);
    assert!(Arc::get_mut(&mut x).is_none());
}


use std::any::Any;
use std::sync::Arc;
fn print_if_string(value: Arc) {
    if let Ok(string) = value.downcast::() {
        println!("String ({}): {}", string.len(), string);
    }
}
fn main(){
    let my_string = "Hello World".to_string();
    print_if_string(Arc::new(my_string));
    print_if_string(Arc::new(0i8));
}

make_mut(this: &mut Arc<T>) -> &mut T - Делает изменяемую ссылку в данную Arc.

Если есть другие указатели Arc или Weak к одному и тому же значению, make_mut вызовет клон на внутреннем значении, Это также называется клонированием на запись.


use std::sync::Arc;
fn main(){
    let mut data = Arc::new(5);

    *Arc::make_mut(&mut data) += 1;         // Ничего не клонируется.
    let mut other_data = Arc::clone(&data); // Не клонирует внутренние данные
    *Arc::make_mut(&mut data) += 1;         // Клонирует внутренние данные
    *Arc::make_mut(&mut data) += 1;         // Ничего не клонируется.
    *Arc::make_mut(&mut other_data) *= 2;   // Ничего не клонируется.

    // Теперь `data` и `other_data` указывают на разные значения.
    assert_eq!(*data, 8);
    assert_eq!(*other_data, 12);
}

into_inner()->Option<T> - Расходует и возвращает внутреннее значение, если существует ровно одна strong ссылка.

try_unwrap(Arc<T>)->Result<T, Arc<T>> - Возвращает внутреннее значение, если у него Arc ровно одна strong ссылка

unwrap_or_clone()->T - разверните в T при единственной ссылке. В противном случае верните клон T

Возвращает содержащееся значение, если Arc имеет ровно одну сильную ссылку. В противном случае возвращается Err с данными


use std::sync::Arc;
fn main(){
    let x = Arc::new(3);
    assert_eq!(Arc::try_unwrap(x), Ok(3));

    let x = Arc::new(4);
    let _y = Arc::clone(&x);
    println!("{:?}",Arc::try_unwrap(x));// Err(4)
}


use std::sync::Arc;
fn main(){
    let x = Arc::new(3);
    let y = Arc::clone(&x);
    let x_thread = std::thread::spawn(|| Arc::into_inner(x));
    let y_thread = std::thread::spawn(|| Arc::into_inner(y));
    let x_inner_value = x_thread.join().unwrap();
    let y_inner_value = y_thread.join().unwrap();
    // Один из потоков гарантированно получит внутреннее значение:
    assert!(matches!(
        (x_inner_value, y_inner_value),
        (None, Some(3)) | (Some(3), None)
    ));
}


fn main(){
    #![feature(arc_unwrap_or_clone)]
    let inner = String::from("test");
    let ptr = inner.as_ptr();

    let arc = Arc::new(inner);
    let inner = Arc::unwrap_or_clone(arc);
    // The inner value was not cloned
    assert!(ptr::eq(ptr, inner.as_ptr()));

    let arc = Arc::new(inner);
    let arc2 = arc.clone();
    let inner = Arc::unwrap_or_clone(arc);
    // Because there were 2 references, we had to clone the inner value.
    assert!(!ptr::eq(ptr, inner.as_ptr()));
    // `arc2` is the last reference, so when we unwrap it we get back
    // the original `String`.
    let inner = Arc::unwrap_or_clone(arc2);
    assert!(ptr::eq(ptr, inner.as_ptr()));
}

strong_count(this: &Arc<T>) -> usize

weak_count(this: &Arc<T>) -> usize

decrement_strong_count() - Уменьшает счетчик strong ссылок Arc<T> на единицу

increment_strong_count() - Увеличивает счетчик strong ссылок Arc<T> на единицу

Получает количество сильных указателей strong (Arc) для этого значения.


use std::sync::Arc;
fn main(){
    let five = Arc::new(5);
    let _also_five = Arc::clone(&five);
    assert_eq!(2, Arc::strong_count(&five));
}

Получает количество слабых указателей Weak на это значение.


use std::sync::Arc;
fn main(){
    let five = Arc::new(5);
    assert_eq!(1, Arc::weak_count(&five));
}

ptr_eq(this: &Arc<T>, other: &Arc<T>) -> bool - Возвращает true, если две Arc указывают на одно и то же значение


use std::sync::Arc;
fn main(){
    let five = Arc::new(5);
    let same_five = Arc::clone(&five);
    let other_five = Arc::new(5);
    assert!(Arc::ptr_eq(&five, &same_five));
    assert!(!Arc::ptr_eq(&five, &other_five));
}

Доступ к этим значениям может занять нетривиальное количество времени. Если к нескольким таким значениям обычно обращаются вместе, может быть лучше поместить их в одну оболочку.

struct S { ❌ 
    x: Arc<Mutex<u32>>,
    y: Arc<Mutex<u32>>,
}

// может быть лучше представлено так:

struct S {✅
    xy: Arc<Mutex<(u32, u32)>>,
}

Как правило, вам не следует указывать Rc<RefCell<T>> (или Arc<Mutex<T>>) в API, а лучше сделать их внутренней деталью реализации.

Как правило, вам не следует указывать Rc<RefCell<T>> (или Arc<Mutex<T>>) в API, а лучше сделать их внутренней деталью реализации. Поступая таким образом, вы получаете полный контроль над всеми блокирующими областями внутри ваших методов (ни одна область не может расширяться за пределы), поэтому убедитесь, что пересечения не произойдет, и предоставьте полностью безопасный API.


fn main(){
    let owner1 = Arc::new(Mutex::new("string"));
    let owner2 = owner1.clone();
    {
        let value = owner1.lock.unwrap();
        // Никакого пересечения, поскольку область блокировки owner1 на этом заканчивается.
       // Но это должно быть внутри вашего API, а не то как его должны использовать клиенты
    }
    {
        let value = owner2.lock.unwrap();
    }
}

Спрятать внутрь реализации


#[derive(Clone)]
struct SharedString(Arc>);

impl SharedString {
    fn mutate_somehow(&self) {
        let mut val = self.lock.unwrap();
        *val = "another string"
    }
}
fn main(){
    let owner1 = SharedString(Arc::new(Mutex::new("string")));
    let owner2 = owner1.clone();

    // Здесь мы изменяем одно и то же значение, 
    // но пересечение областей блокировки не может произойти по замыслу. 
    // Такой API никогда не будет блокироваться или паниковать 
    // из-за нарушения правил заимствования во время выполнения.

    owner1.mutate_somehow();
    owner2.mutate_somehow();
}