Rustのスレッドローカル変数について
Rustには2種類のスレッドローカル変数がある。
#[thread_local]
属性
#[thread_local]
属性は、指定した static
アイテムをスレッドローカルにするようにLLVMに伝える。これによりC言語のスレッドローカル変数と同様に、ELFリンカ側の処理によりスレッドローカル変数が実現される。
この方法には以下のような問題点がある。
- 一部のプラットフォームでは利用できない。
- 現状では、不要な
Sync
が要求される。 - 参照を取得後にスレッドが終了するとUBになるため健全性が保証されていない。
- 以上のような理由があってか、安定入りしていない。そもそも
thread_local!
があるので廃止してもよいのではないかという意見もある
thread_local!
マクロ
thread_local!
マクロは、スレッドローカル変数に安全にアクセスするためのラッパーを生成する。例えば、
thread_local! { static X : Cell<u32> = Cell::new(0); }
と書いたら、実際には以下のようなアイテムが生成される。
static X : LocalKey<Cell<u32>> = LocalKey { ... };
thread_local!
は、もともとN:Mの並行性モデルを採用していたRustがグリーンスレッドを廃止して1:1に移行するにあたり、タスクローカル変数を提供するための local_data!
を整理する形でできたものである。現在のネイティブスレッドの観点からも、このマクロ以下の点で優れているといえる。
- 参照の有効期間が
LocalKey::with
により制限されるため、健全性の問題が解決されている。 - ELFリンカに依存したスレッドローカル領域が提供されていない場合も、OS非依存の代替手段が利用される。
オブジェクトごとのスレッドローカル変数
以上とは別の話だが、オブジェクトごとにスレッドローカルなフィールドを持つ必要がある場合は thread_local
crate というライブラリが使えるようだ。
まとめ
Rustでスレッドローカル変数を使うときは、 thread_local!
マクロを使うのが望ましい。これは LocalKey
という安全で移植性の高いラッパーを提供している。