簡體   English   中英

線程中使用的所有內容都必須在 Rust 中“發送”嗎?

[英]Must everything used in thread be `Send`able in Rust?

我正在嘗試在 Rust 中實現並發處理。 這是(簡化的)代碼( 游樂場):

struct Checker {
    unsendable: std::rc::Rc<String> // `Rc` does not implement Send + Sync (`Rc` just for simplicity, a graph actually)
}

impl Checker {
    pub fn check(&self, input: String) -> bool {
        // some logics (no matter)
        true
    }
}

struct ParallelChecker {
    allow_checker: Checker,
    block_checker: Checker
}

impl ParallelChecker {
    // `String` for simplicity here
    pub fn check(&self, input: String) -> bool {
        let thread_pool = rayon::ThreadPoolBuilder::new()
            .num_threads(2)
            .build()
            .unwrap();

        // scoped thread pool for simplicity (to avoid `self` is not `'static`)
        thread_pool.scope(move |s| {
            s.spawn(|_| {
                let result = self.allow_checker.check(input.clone());
                // + send result via channel
            });
        });

        thread_pool.scope(move |s| {
            s.spawn(|_| {
                let result = self.block_checker.check(input.clone());
                // + send result via channel
            });
        });

        true // for simplicity
        // + receive result form the channels
    }
}

問題是self.allow_checkerself.block_checker沒有實現Send

55 |         thread_pool.scope(move |s| {
   |                     ^^^^^ `Rc<String>` cannot be sent between threads safely
   |
   = help: within `Checker`, the trait `Send` is not implemented for `Rc<String>`
   = note: required because it appears within the type `Checker`
   = note: required because of the requirements on the impl of `Send` for `Mutex<Checker>`
   = note: 1 redundant requirements hidden
   = note: required because of the requirements on the impl of `Send` for `Arc<Mutex<Checker>>`
   = note: required because it appears within the type `[closure@src/parallel_checker.rs:55:27: 60:10]`

我的印象是只有通過渠道發送的東西必須實現Send + Sync ,我在這里可能錯了。

如您所見,線程代碼沒有任何共享變量( self除外)。 如何在不實施Send且不支付同步費用的情況下使其正常工作?

我試圖同步訪問(雖然它看起來沒用,因為線程中沒有共享變量),但沒有運氣:

    let allow_checker = Arc::new(Mutex::new(self.allow_checker));
        thread_pool.scope(move |s| {
            s.spawn(|_| {
                let result = allow_checker.lock().unwrap().check(input.clone());
                // + send result via channel
            });
        });

PS。 由於性能問題以及大量相關代碼也將遷移到Arc ,因此非常不希望遷移到Arc

我的印象是只有通過渠道發送的東西必須實現Send + Sync ,我在這里可能錯了。

有點錯誤:

  • 如果其值可以跨線程發送,則該類型為Send 例如,許多類型是SendString是。
  • 如果可以從多個線程訪問對其值的引用而不會引起任何數據爭用,則該類型為Sync 也許令人驚訝的是,這意味着StringSync ——由於在共享時是不可變的——並且通常T對於任何作為Send&T都是Sync

請注意,這些規則不關心值如何跨線程發送或共享,只關心它們。

這在這里很重要,因為用於啟動線程的閉包本身是跨線程發送的:它在“啟動器”線程上創建,並在“啟動”線程上執行。

因此,這個閉包必須是Send ,這意味着它捕獲的任何東西都必須是Send

為什么Rc不實現Send

因為它的引用計數是非原子的。

也就是說,如果您有:

  • 線程 A:一個Rc
  • 線程 B:一個Rc (相同的指針)。

然后線程 A 在線程 B 創建克隆的同時丟棄其句柄,您希望計數為 2(仍然),但由於非原子訪問,盡管仍然存在 2 個句柄,但它可能只有 1:

  • 線程 A 讀取計數 (2)。
  • 線程 B 讀取計數 (2)。
  • 線程 B 寫入遞增計數 (3)。
  • 線程 A 寫入遞減計數 (1)。

然后,下一次 B 放下把手時,物品被破壞,memory 被釋放,任何通過最后一個把手的進一步訪問都會炸毀你的臉。

我試圖同步訪問(雖然它看起來沒用,因為線程中沒有共享變量),但沒有運氣:

您不能包裝類型以使其成為Send ,它沒有幫助,因為它不會更改基本屬性。

即使Rc包含在Arc<Mutex<...>>中,上述Rc上的競爭條件也可能發生。

因此!Send具有傳染性並“感染”任何包含類型。

由於性能問題以及大量相關代碼也將遷移到Arc ,因此非常不希望遷移到Arc

Arc本身的性能開銷相對較小,因此除非您繼續克隆那些Checker ,否則它似乎不太重要,您可能會對其進行改進——傳遞引用而不是克隆。

如果Checker不是Sync ,這里更高的開銷將來自Mutex (或RwLock )。 正如我所提到的,任何不可變的值都是微不足道的Sync ,所以如果你可以將Checker的內部 state 重構為Sync ,那么你可以完全避免Mutex並且只讓Checker包含Arc<State>

如果您目前有可變的 state,請考慮將其提取出來,朝着:

struct Checker {
    unsendable: Arc<Immutable>,
}

impl Checker {
    pub fn check(&self, input: String) -> Mutable {
        unimplemented!()
    }
}

或者放棄並行檢查的想法。

Send的要求不限於頻道。 如果您查看thread::spawn的文檔,您會看到它采用的閉包必須是Send ,因此只能捕獲Send值。 這同樣適用於rayon::ThreadPool::scope的閉包。

由於您正在克隆閉包中的字符串,因此您不妨在生成線程之前克隆它們:

let input_clone = input.clone();
thread_pool.scope(move |s| {
    s.spawn(|_| {
        let result = self.allow_checker.check(input_clone);
        // + send result via channel
    });
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM