亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何將對堆棧變量的引用傳遞給線程?

如何將對堆棧變量的引用傳遞給線程?

如何將對堆棧變量的引用傳遞給線程?我正在編寫一個WebSocket服務器,其中一個Web客戶端連接到一個多線程計算機AI上下棋。WebSocket服務器希望傳遞一個Logger對象進入人工智能代碼。這個Logger對象將把日志行從AI排到Web客戶端。這個Logger必須包含對客戶端連接的引用。我對生命如何與線程交互感到困惑。我用一個Wrapper由類型參數化的結構。這個run_thread函數嘗試展開該值并將其記錄下來。use std::fmt::Debug;use std::thread;struct Wrapper<T: Debug> {     val: T,}fn run_thread<T: Debug>(wrapper: Wrapper<T>) {     let thr = thread::spawn(move || {         println!("{:?}", wrapper.val);     });     thr.join();}fn main() {     run_thread(Wrapper::<i32> { val: -1 });}這個wrapper參數存在于堆棧中,且其生存期不會延長。run_thread的堆??蚣?,即使在堆棧幀結束之前線程將被連接。我可以從堆棧中復制值:use std::fmt::Debug;use std::thread;struct Wrapper<T: Debug + Send> {     val: T,}fn run_thread<T: Debug + Send + 'static>(wrapper: Wrapper<T>) {     let thr = thread::spawn(move || {         println!("{:?}", wrapper.val);     });     thr.join();}fn main() {     run_thread(Wrapper::<i32> { val: -1 });}如果T是對一個我不想復制的大對象的引用:use std::fmt::Debug;use std::thread;struct Wrapper<T: Debug + Send> {     val: T,}fn run_thread<T: Debug + Send + 'static>(wrapper: Wrapper<T>) {     let thr = thread::spawn(move || {         println!("{:?}", wrapper.val);     });     thr.join();}fn main() {     let mut v = Vec::new();     for i in 0..1000 {         v.push(i);     }     run_thread(Wrapper { val: &v });}其結果是:error: `v` does not live long enough   --> src/main.rs:22:32    | 22 |     run_thread(Wrapper { val: &v });    |                                ^ does not live long enough 23 | }    | - borrowed value only lives until here    |    = note: borrowed value must be valid for the static lifetime...在我真正的程序中,看起來兩個Logger連接對象必須放置在Arc包裝紙??蛻舳吮灰髮⑦B接放在Arc當代碼被并行化時,代碼是庫內部的。這尤其令人討厭,因為連接的生存期肯定大于工作線程的生存期。我錯過了什么嗎?
查看完整描述

2 回答

?
隔江千里

TA貢獻1906條經驗 獲得超10個贊

標準庫中的線程支持允許創建的線程比創建線程的線程更長;這是一件好事!但是,如果要將對堆棧分配變量的引用傳遞給這些線程之一,則無法保證該變量在線程執行時仍然有效。在其他語言中,這將允許線程訪問無效內存,從而產生大量內存安全問題。

幸運的是,我們并不局限于標準庫。至少有兩個箱子作用域螺紋-保證在某個作用域結束之前退出的線程。這可以確保堆棧變量在線程的整個期間都可用:

還有一些板條箱可以抽象出“線程”的底層細節,但允許您完成目標:

下面是每一個例子。每個示例生成大量線程,并在沒有鎖定、沒有鎖定的情況下對本地向量進行變異。Arc也沒有克隆。請注意,該突變具有sleep調用以幫助驗證這些呼叫是并行進行的。

您可以擴展這些示例,以共享對實現的任何類型的引用。Sync,如Mutex或者Atomic*..然而,使用這些將引入鎖定。

范圍-線程池

use scoped_threadpool::Pool; // 0.1.9use std::{thread, time::Duration};fn main() {
    let mut vec = vec![1, 2, 3, 4, 5];
    let mut pool = Pool::new(vec.len() as u32);

    pool.scoped(|scoped| {
        for e in &mut vec {
            scoped.execute(move || {
                thread::sleep(Duration::from_secs(1));
                *e += 1;
            });
        }
    });

    println!("{:?}", vec);}

橫梁

use crossbeam; // 0.6.0use std::{thread, time::Duration};fn main() {
    let mut vec = vec![1, 2, 3, 4, 5];

    crossbeam::scope(|scope| {
        for e in &mut vec {
            scope.spawn(move |_| {
                thread::sleep(Duration::from_secs(1));
                *e += 1;
            });
        }
    })
    .expect("A child thread panicked");

    println!("{:?}", vec);}

人造絲

use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; // 1.0.3use std::{thread, time::Duration};fn main() {
    let mut vec = vec![1, 2, 3, 4, 5];

    vec.par_iter_mut().for_each(|e| {
        thread::sleep(Duration::from_secs(1));
        *e += 1;
    });

    println!("{:?}", vec);}

客戶端必須將連接放在Arc當代碼被并行化時,代碼是庫內部的。

也許你能更好地隱藏你的并行性?您能否接受記錄器,然后將其包裝在Arc / Mutex在把它交給你的線程之前?


查看完整回答
反對 回復 2019-06-25
?
慕森王

TA貢獻1777條經驗 獲得超3個贊

我的解決辦法是Logger實施Clone,并有一個類型為Arc<Mutex<Connection>>..然后,用戶可以將記錄器的一個克隆傳遞給線程代碼。用戶無法轉移Connection對于線程代碼(用戶需要它用于其他目的),因此我認為線程代碼不可能方便地執行Arc并代表用戶進行裝箱。 

查看完整回答
反對 回復 2019-06-25
  • 2 回答
  • 0 關注
  • 594 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號