[英]Swift UIApplication.delegate must be used from main thread only
[英]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_checker
和self.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
。 例如,許多類型是Send
, String
是。Sync
。 也許令人驚訝的是,這意味着String
是Sync
——由於在共享時是不可變的——並且通常T
對於任何作為Send
的&T
都是Sync
。請注意,這些規則不關心值如何跨線程發送或共享,只關心它們。
這在這里很重要,因為用於啟動線程的閉包本身是跨線程發送的:它在“啟動器”線程上創建,並在“啟動”線程上執行。
因此,這個閉包必須是Send
,這意味着它捕獲的任何東西都必須是Send
。
為什么
Rc
不實現Send
?
因為它的引用計數是非原子的。
也就是說,如果您有:
Rc
。Rc
(相同的指針)。然后線程 A 在線程 B 創建克隆的同時丟棄其句柄,您希望計數為 2(仍然),但由於非原子訪問,盡管仍然存在 2 個句柄,但它可能只有 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.