Rust Cheet Sheet(アトミック操作)
Written on 2024-12-19
Rustのアトミック操作についての自分のためのまとめ。 『詳解 Rustアトミック操作とロック』の読書メモ。
Box
値をヒープに確保する。
Box::leak():確保された領域を開放しない。'staticと互換性があるプログラム終了までのライフタイムを持つ。
Rc
参照カウント。シングルスレッドでしか使えない。Arcより軽い。
clone()しても中身は複製されない。
Arc
「アトミック」参照カウント。他のスレッドに移動できる。CPUがアトミック操作をサポートしている必要がある。Rcよりも重い。
Cell
内部可変性を提供する。
- シングルスレッドしか使えない
- 値を変更するときは、
.take()で、現在の値を得て、Cellの中身は空に置き換える。- 得られた値を変更して、
.set()で変更された値をCellに書き戻す。
RefCell
参照カウンタを使ったCell。Cellよりも重い。
- シングルスレッドしか使えない
- 他にだれも可変借用していなければ、可変借用できる。
- 可変借用を使えば、内容を直接変更できる。
- 普遍借用は複数できる。
UnsafeCell
CellやMutexの実装に使われる。.get()で生ポインタを取ることができるが、それはunsafe内で扱う必要がある。
Send
トレイト。別のスレッドに送ることができる。
Sync
- トレイト。別のスレッドと共有することができる。
&TがSendならばTはSync。
例
- プリミティブ型(
u32,bool,strなど)はSendかつSync。 - すべてのフィールドが
SendかつSyncな構造体はSendかつSync(トレイトが自動で実装される)。- トレイトを自動で実装したくない場合は、
PhantomData<T>のTにSend,Syncでない型を含めたメンバーをもたせる。PhantomDataはコンパイラの型推論に使われるだけで、実際のメモリを消費しない。
- トレイトを自動で実装したくない場合は、
Arc<i32>はSend。Rc<i32>はSendではない。Cell<i32>はSendだがSyncではない。- 明示的に実装したい場合は
unsafe impl Send for X{},unsafe impl Sync for X{}と実装することができる。Send,Syncしたときの安全性はコンパイラは保証してくれず、プログラマが保証しなければならないのでunsafe。
ロック: MutexとRwLock
Mutexはロックされていない状態とロックされている状態を取る。- ロックされているときは排他的なアクセス(読み書き)が可能。
lock()は、ロックが取れるまで待ち続ける。lock()はロックが取れたらMutexGuardを返す。Deref,DerefMutトレイトが実装されてるので中のデータに透過的にアクセスできる。
try_lock()はロックが取れたらResult<MutexGuard<T>>を返し、取れなかったらErrを返す。- ロックがスコープを外れて
Dropしたときに自動でアンロックされる。- スコープが終わらないうちに
Dropしたいときはdrop()を使う。 list.lock().unwrap().push(2);のイデオムは、その場でロックを取得してメンバ関数を実行し、その行でロックをドロップする。
- スコープが終わらないうちに
get_mut()はロックを取ったうえでミュータブルな参照を返す。into_inner()はMutexを消費する。つまり、所有権を取得して中身を返す。Mutexによる保護は失われ、以後、他のスレッドはlock()することができない。
RwLockはロックしたときに、共有参照(&T)と排他参照(&mut T)を返す。read()はリードロック(共有参照)write()はライトロック(排他参照)RefCellのマルチスレッド版
Mutexがドロップするスコープについて注意が必要。
list.lock().unwrap().push(2); // ロックはこの行でドロップされる
if let Some(item) = list.lock().unwrap().pop() {
process_item(item);
} // ロックはこの行まで確保されたまま
if list.lock().unwrap().pop() == Some(1) { // ロックはこの行でドロップされる
do_something;
}
このように書けば良い。
let item = list.lock().unwrap().pop(); // ロックはこの行でドロップされる
if let Some(item) = item {
process_item(item);
}
アトミック操作
load(),store()操作はCortex-M0+でもサポートされている。fetch_add(),compare_exchange()操作はCortex-M0+ではサポートされていない。- Cortex-M3以上ではサポートされている。
- アトミック変数は内部可変性を持つ。
mutでなくても、store()操作が可能。- スレッド間で共有可能。
'staticにすることもできる。
- アトミック変数は
Copyを実装していない。
メモリ・オーダリング
Relaxed: 一つのアトミック変数に対する操作順序が一貫している。複数の変数の場合は順序は保証されない。Release/Acquire:Releaseストアが、Acquireロードに先行する。つまり、Releaseより先の現象はAcqireより後で観測できる。AcqRel:AcquireとReleaseの組み合わせ。