Поток
Работа с потоками на традиционных языках программирования требует некоторой заботы об общей памяти. Проблема может возникнуть, когда один или несколько потоков одновременно изменяют общие данные, вызывая ошибку.
Rust работает в так называемом потокобезопасном режиме, он не обменивается данными между потоками и просто допускает потокобезопасные операции. Похоже, что-то вроде стратегии владения, которая предотвращает ошибки в памяти, а также позволяет писать безопасные параллельные программы.
Прежде чем перейти к основной теме статьи, необходимо просмотреть некоторые темы, чтобы постепенно преуменьшать содержание.
Что такое Box<T>
?
Box
- это, по сути, тип не копируемого указателя с определенным размером в стеке с типом дженерика T
в куче. Box
можно использовать для:
- Когда необходимо знать размер типа во время компиляции
- Когда требуется передать право собственности на большой объем данных, и они не могут быть скопированы
- When должен знать, реализует ли тип приватную характеристику.
- И так далее…
Компилятор Rust должен знать размер объектов во время компиляции, поэтому определение структуры с переменным размером может быть проблемой для компилятора. Чтобы решить это, необходимо обернуть проблемный тип в поле, которое имеет фиксированный тип в стеке, оно будет указывать на кучу, так как данные могут свободно расти.
Стек в Rust имеет ограничение по объему памяти в 2 МБ (по умолчанию) и заполняется во время компиляции.
В этом примере Block
инкапсулирует другие Block
внутри себя, что делает невозможным определение общего размера блокчейна. Это связано с тем, что блокчейны могут содержать “n” блоков.
enum Blockchain {
Block(u32, Box<Blockchain>),
End
}
use Blockchain::{Block, End};
fn main() {
let blockchain = Block(0,
Box::new(Block(1,
Box::new(Block(2,
Box::new(End))))));
}
Что такое Rc<T>
?
Rc
расшифровывается как подсчет ссылок, и он используется в Rust для того, чтобы сделать возможным, что одно значение имеет несколько владельцев. Поведение Rc
, как следует из названия, отслеживает количество ссылок на значение, что определяет, используется ли значение по-прежнему. В случае, если номер ссылки равен нулю, значение может быть произвольным.
Важно сказать, что Rc
- однопоточный.
Этот пример похож на предыдущий, однако в этом примере основной блокчейн передает свое право собственности другим.
use std::rc::Rc;
enum Blockchain {
Block(u32, Rc<Blockchain>),
End
}
use Blockchain::{Block, End};
fn main() {
let blockchain_main = Rc::new(Block(0,
Rc::new(Block(1,
Rc::new(Block(2,
Rc::new(End)))))));
let blockchain_a = Block(10, Rc::clone(&blockchain_main));
let blockchain_b = Block(100, Rc::clone(&blockchain_main));
}
Что такое Arc<T>
?
Arc
- это тип из подмодуля std::sync
, и с его помощью можно обрабатывать совместно изменяемое состояние с помощью потоков. Arc
расшифровывается как подсчет атомарных ссылок, это похоже на Rc
, подсчет ссылок, который мы видели ранее, но с возможностью работы с потоками.
В следующем примере блокчейн создается в основной функции и клонируется в каждый порожденный поток, после чего каждый из них создает свой блокчейн на основе основного.
use std::thread;
use std::sync::Arc;
enum Blockchain {
Block(u32, Arc<Blockchain>),
End
}
use Blockchain::{Block, End};
fn main() {
let blockchain = Arc::new(
Block(0,
Arc::new(Block(1,
Arc::new(Block(2,
Arc::new(End)))))
));
for t in 3..10 {
let clone = blockchain.clone();
thread::spawn(move || {
let _blockchain_thread = Block(t, clone);
}).join().unwrap();
}
}
Это статья изучение темы, поэтому какая-то информация может быть слишком сжата, но я думаю, что она может быть кому-то полезна.