サーチ…


前書き

なぜ特定のものがRustでunsafeではunsafeとマークされているのか、なぜ特定の(まれな)状況でこのエスケープハッチを使用する必要があるのか​​を説明してください。

データレース

データ競合は、あるパーティによってメモリが更新され、別のパーティによってメモリが更新されたときに発生します。共有カウンタを使用したデータ競争の古典的な例を見てみましょう。

use std::cell::UnsafeCell;
use std::sync::Arc;
use std::thread;

// `UnsafeCell` is a zero-cost wrapper which informs the compiler that "what it
// contains might be shared mutably." This is used only for static analysis, and
// gets optimized away in release builds.
struct RacyUsize(UnsafeCell<usize>);

// Since UnsafeCell is not thread-safe, the compiler will not auto-impl Sync for
// any type containig it. And manually impl-ing Sync is "unsafe".
unsafe impl Sync for RacyUsize {}

impl RacyUsize {
    fn new(v: usize) -> RacyUsize {
        RacyUsize(UnsafeCell::new(v))
    }

    fn get(&self) -> usize {
        // UnsafeCell::get() returns a raw pointer to the value it contains
        // Dereferencing a raw pointer is also "unsafe"
        unsafe { *self.0.get() }
    }

    fn set(&self, v: usize) { // note: `&self` and not `&mut self`
        unsafe { *self.0.get() = v }
    }
}

fn main() {
    let racy_num = Arc::new(RacyUsize::new(0));

    let mut handlers = vec![];
    for _ in 0..10 {
        let racy_num = racy_num.clone();
        handlers.push(thread::spawn(move || {
            for i in 0..1000 {
                if i % 200 == 0 {
                    // give up the time slice to scheduler
                    thread::yield_now();
                    // this is needed to interleave the threads so as to observe
                    // data race, otherwise the threads will most likely be
                    // scheduled one after another.
                }

                // increment by one
                racy_num.set(racy_num.get() + 1);
            }
        }));
    }

    for th in handlers {
        th.join().unwrap();
    }

    println!("{}", racy_num.get());
}

マルチコアプロセッサで実行すると、出力はほぼ10000 (10スレッド×1000)未満になります。

この例では、データ競争は論理的に間違っていますが、依然として意味のある価値を生み出しています。これは、レースに1つの単語しか関与していなかったため、更新によって部分的に変更されていない可能性があるからです。しかし、一般的に、データ競合は、オブジェクトが競合して複数の単語にまたがる場合や、ポインターが関わっているときに無効なメモリー位置(メモリーが安全ではない)を示す値を生成した場合に、タイプに対して無効な破損値(安全でないタイプ)

しかし、原子プリミティブを慎重に使用することで、非常に効率的なデータ構造の構築が可能になります。内部的には、これらの「安全でない」操作のいくつかを実行して、Rustのタイプシステムによって静的に検証可能ではないアクションを実行する必要があります。抽象化)。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow