チャネル、ロック、同期
同期プリミティブのランタイム固有性に関する注記
同期のものを使うのではなく、非同期プリミティブが必要な理由
チャネル
- 基本的に std のものと同じだが、await する
- タスク間で通信する(同じスレッドまたは別のスレッド)
- ワンショット
- mpsc
- その他のチャネル
- 有界チャネルと無界チャネル
ロック
- 非同期 Mutex
- 比較: std::Mutex - await ポイントをまたいで保持できる(ガード内で mutex を借用する、ガードは Send、スケジューラ対応? それとも単に lock が async だから?)、lock は async(ロックが利用可能になるのを待つ間スレッドをブロックしない)
- await をまたいでガードを保持することに対する Clippy lint さえある(https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock)
- await をまたいで保持できるため、より高コスト
- 可能なら std::Mutex を使う
- try_lock を使える、または mutex が競合状態にならないと想定される
- 可能なら std::Mutex を使う
- yield しても lock が魔法のように drop されるわけではない(それこそが lock の要点のようなもの!)
- await をまたいで mutex を保持することによるデッドロック
- タスクはデッドロックするが、他のタスクは進行できるため、プロセス統計/ツール/OS ではデッドロックのように見えない可能性がある
- 通常の助言 - スコープを限定する、ロックを最小化する、ロックの順序を決める、代替手段を優先する
- mutex poisoning はない
- lock_owned
- blocking_lock
- async では使用できない
- 他のロックにも当てはまる(上記は mutex を具体的に論じる前に移動すべきか? おそらくそう)
- 比較: std::Mutex - await ポイントをまたいで保持できる(ガード内で mutex を借用する、ガードは Send、スケジューラ対応? それとも単に lock が async だから?)、lock は async(ロックが利用可能になるのを待つ間スレッドをブロックしない)
- RWLock
- Semaphore
- yield
その他の同期プリミティブ
- notify、barrier
- OnceCell
- atomics