成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

在 Rust 編程中使用多線程

開(kāi)發(fā)
編程語(yǔ)言有一些不同的方法來(lái)實(shí)現(xiàn)線程,而且很多操作系統(tǒng)提供了創(chuàng)建新線程的 API。Rust 標(biāo)準(zhǔn)庫(kù)使用 1:1 線程實(shí)現(xiàn),這代表程序的每一個(gè)語(yǔ)言級(jí)線程使用一個(gè)系統(tǒng)線程。

1. Rust線程實(shí)現(xiàn)理念

在大部分現(xiàn)代操作系統(tǒng)中,已執(zhí)行程序的代碼在一個(gè) 進(jìn)程(process)中運(yùn)行,操作系統(tǒng)則會(huì)負(fù)責(zé)管理多個(gè)進(jìn)程。在程序內(nèi)部,也可以擁有多個(gè)同時(shí)運(yùn)行的獨(dú)立部分。這些運(yùn)行這些獨(dú)立部分的功能被稱(chēng)為 線程(threads)。例如,web 服務(wù)器可以有多個(gè)線程以便可以同時(shí)響應(yīng)多個(gè)請(qǐng)求。

將程序中的計(jì)算拆分進(jìn)多個(gè)線程可以改善性能,因?yàn)槌绦蚩梢酝瑫r(shí)進(jìn)行多個(gè)任務(wù),不過(guò)這也會(huì)增加復(fù)雜性。因?yàn)榫€程是同時(shí)運(yùn)行的,所以無(wú)法預(yù)先保證不同線程中的代碼的執(zhí)行順序。這會(huì)導(dǎo)致諸如此類(lèi)的問(wèn)題:

  • 競(jìng)態(tài)條件(Race conditions),多個(gè)線程以不一致的順序訪問(wèn)數(shù)據(jù)或資源。
  • 死鎖(Deadlocks),兩個(gè)線程相互等待對(duì)方,這會(huì)阻止兩者繼續(xù)運(yùn)行。
  • 只會(huì)發(fā)生在特定情況且難以穩(wěn)定重現(xiàn)和修復(fù)的 bug。

Rust 嘗試減輕使用線程的負(fù)面影響。不過(guò)在多線程上下文中編程仍需格外小心,同時(shí)其所要求的代碼結(jié)構(gòu)也不同于運(yùn)行于單線程的程序。

編程語(yǔ)言有一些不同的方法來(lái)實(shí)現(xiàn)線程,而且很多操作系統(tǒng)提供了創(chuàng)建新線程的 API。Rust 標(biāo)準(zhǔn)庫(kù)使用 1:1 線程實(shí)現(xiàn),這代表程序的每一個(gè)語(yǔ)言級(jí)線程使用一個(gè)系統(tǒng)線程。

2.使用spawn創(chuàng)建新線程

為了創(chuàng)建一個(gè)新線程,需要調(diào)用 thread::spawn 函數(shù)并傳遞一個(gè)閉包, 并在其中包含希望在新線程運(yùn)行的代碼。看下面的例子:

use std::thread;
use std::time::Duration;

fn main() {
    thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
}

注意當(dāng) Rust 程序的主線程結(jié)束時(shí),新線程也會(huì)結(jié)束,而不管其是否執(zhí)行完畢。這個(gè)程序的輸出可能每次都略有不同,不過(guò)它大體上看起來(lái)像這樣:

thread::sleep 調(diào)用強(qiáng)制線程停止執(zhí)行一小段時(shí)間,這會(huì)允許其他不同的線程運(yùn)行。這些線程可能會(huì)輪流運(yùn)行,不過(guò)并不保證如此:這依賴(lài)操作系統(tǒng)如何調(diào)度線程。在這里,主線程首先打印,即便新創(chuàng)建線程的打印語(yǔ)句位于程序的開(kāi)頭,甚至即便我們告訴新建的線程打印直到 i 等于 9,它在主線程結(jié)束之前也只打印到了 5。

如果運(yùn)行代碼只看到了主線程的輸出,或沒(méi)有出現(xiàn)重疊打印的現(xiàn)象,嘗試增大區(qū)間 (變量 i 的范圍) 來(lái)增加操作系統(tǒng)切換線程的機(jī)會(huì)。

3.使用join等待所有線程結(jié)束

由于主線程結(jié)束,上面演示的代碼大部分時(shí)候不光會(huì)提早結(jié)束新建線程,因?yàn)闊o(wú)法保證線程運(yùn)行的順序,甚至不能實(shí)際保證新建線程會(huì)被執(zhí)行!

可以通過(guò)將 thread::spawn 的返回值儲(chǔ)存在變量中來(lái)修復(fù)新建線程部分沒(méi)有執(zhí)行或者完全沒(méi)有執(zhí)行的問(wèn)題。thread::spawn 的返回值類(lèi)型是 JoinHandle。JoinHandle 是一個(gè)擁有所有權(quán)的值,當(dāng)對(duì)其調(diào)用 join 方法時(shí),它會(huì)等待其線程結(jié)束。

看下面的示例代碼:

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    handle.join().unwrap();
}

通過(guò)調(diào)用 handle 的 join 會(huì)阻塞當(dāng)前線程直到 handle 所代表的線程結(jié)束。阻塞(Blocking)線程意味著阻止該線程執(zhí)行工作或退出。因?yàn)槲覀儗?join 調(diào)用放在了主線程的 for 循環(huán)之后,編譯這段代碼后運(yùn)行結(jié)果如下:

這兩個(gè)線程仍然會(huì)交替執(zhí)行,不過(guò)主線程會(huì)由于 handle.join() 調(diào)用會(huì)等待直到新建線程執(zhí)行完畢。

不過(guò)如果將 handle.join() 移動(dòng)到 main 中 for 循環(huán)之前會(huì)發(fā)生什么呢,看下面的代碼:

use std::thread;
use std::time::Duration;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    handle.join().unwrap();

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
}

代碼編譯執(zhí)行后結(jié)果如下:

主線程會(huì)等待直到新建線程執(zhí)行完畢之后才開(kāi)始執(zhí)行 for 循環(huán),所以輸出將不會(huì)交替出現(xiàn)。

因此,將join放在代碼的不同地方, 將會(huì)影響線程是否同時(shí)執(zhí)行。

4.將move閉包與線程一起使用

move 關(guān)鍵字經(jīng)常用于傳遞給 thread::spawn 的閉包,因?yàn)殚]包會(huì)獲取從環(huán)境中取得的值的所有權(quán),因此會(huì)將這些值的所有權(quán)從一個(gè)線程傳送到另一個(gè)線程。

為了在新建線程中使用來(lái)自于主線程的數(shù)據(jù),需要新建線程的閉包獲取它需要的值, 下面的代碼展示了一個(gè)嘗試在主線程中創(chuàng)建一個(gè) vector 并用于新建線程的例子,不過(guò)這么寫(xiě)還不能工作, 代碼如下:

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(|| {
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

閉包使用了 v,所以閉包會(huì)捕獲 v 并使其成為閉包環(huán)境的一部分。因?yàn)?thread::spawn 在一個(gè)新線程中運(yùn)行這個(gè)閉包,所以可以在新線程中訪問(wèn) v。然而當(dāng)編譯這個(gè)例子時(shí),會(huì)得到如下錯(cuò)誤:

Rust 會(huì) 推斷 如何捕獲 v,因?yàn)?nbsp;println! 只需要 v 的引用,閉包嘗試借用 v。然而這有一個(gè)問(wèn)題:Rust 不知道這個(gè)新建線程會(huì)執(zhí)行多久,所以無(wú)法知曉對(duì) v 的引用是否一直有效。

看一段比較極端情況的代碼:

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(|| {
        println!("Here's a vector: {:?}", v);
    });

    drop(v); // 壞事兒了!

    handle.join().unwrap();
}

如果 Rust 允許這段代碼運(yùn)行,則新建線程則可能會(huì)立刻被轉(zhuǎn)移到后臺(tái)并完全沒(méi)有機(jī)會(huì)運(yùn)行。新建線程內(nèi)部有一個(gè) v 的引用,不過(guò)主線程立刻就使用drop丟棄了v。接著當(dāng)新建線程開(kāi)始執(zhí)行,v 已不再有效,所以其引用也是無(wú)效的。

為了修復(fù)上面的編譯錯(cuò)誤, 我們可以根據(jù)編譯器給予我們的help嘗試修正一下,如圖:

通過(guò)在閉包之前增加 move 關(guān)鍵字,強(qiáng)制閉包獲取其使用的值的所有權(quán),而不是任由 Rust 推斷它應(yīng)該借用值。

修正后的代碼如下:

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

編譯運(yùn)行試一下:

看起來(lái)沒(méi)問(wèn)題,那么以這個(gè)成功的經(jīng)驗(yàn), 修改那段極端情況的代碼如下:

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });

    drop(v); // 壞事兒了!

    handle.join().unwrap();
}

再次編譯一下看看結(jié)果如何:

Rust編譯器依然沒(méi)有放行, 這個(gè)修復(fù)行不通。

如果為閉包增加 move,將會(huì)把 v 移動(dòng)進(jìn)閉包的環(huán)境中, 因此將不能在主線程中對(duì)其調(diào)用 drop 了, Rust 的所有權(quán)規(guī)則又一次幫助我們杜絕了隱患。因?yàn)?Rust 是保守的并只會(huì)為線程借用 v,這意味著主線程理論上可能使新建線程的引用無(wú)效。通過(guò)告訴 Rust 將 v 的所有權(quán)移動(dòng)到新建線程,我們向 Rust 保證主線程不會(huì)再使用 v。如果對(duì)其作出同樣的move修改, 那么當(dāng)在主線程中使用 v 時(shí)就會(huì)違反所有權(quán)規(guī)則。move 關(guān)鍵字覆蓋了 Rust 默認(rèn)保守的借用,但它不允許我們違反所有權(quán)規(guī)則。

責(zé)任編輯:趙寧寧 來(lái)源: 二進(jìn)制空間安全
相關(guān)推薦

2024-01-09 09:27:57

Rust編程泛型

2011-08-31 16:30:19

Lua多線程

2023-06-15 17:00:11

Rust循環(huán)

2024-03-26 09:25:35

RustSerde重命名

2024-03-06 08:40:16

ReactJavascriptUI交互

2023-05-04 07:33:39

Rust變量常量

2021-03-22 08:45:30

異步編程Java

2013-07-16 10:12:14

iOS多線程多線程概念多線程入門(mén)

2009-10-13 09:56:13

.NET多線程程序

2023-06-13 13:39:00

多線程異步編程

2024-09-06 11:34:15

RustAI語(yǔ)言

2009-03-12 10:52:43

Java線程多線程

2013-06-07 16:30:08

iOS多線程iOS開(kāi)發(fā)NSThread

2011-12-08 10:24:53

JavaNIO

2024-02-07 11:44:20

NestJSRxJS異步編程

2024-04-07 00:00:10

Rust枚舉C代碼

2023-04-02 17:53:10

多線程編程自測(cè)

2009-12-08 12:14:43

2023-06-07 13:49:00

多線程編程C#

2023-06-05 07:56:10

線程分配處理器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 91精品国产乱码麻豆白嫩 | 婷婷色国产偷v国产偷v小说 | 日韩视频在线观看中文字幕 | 成人一区二区三区在线观看 | av天空| 日韩在线视频免费观看 | 国产黄色免费网站 | 中文字幕一区在线观看视频 | 久久视频精品 | 婷婷色网| 成人精品免费视频 | 一级看片免费视频囗交动图 | 日韩视频成人 | 久久综合国产精品 | 伊人精品久久久久77777 | 亚洲h在线观看 | 午夜影晥 | 成人午夜免费在线视频 | 特一级毛片 | 综合网伊人 | 婷婷久久网| 免费中文字幕日韩欧美 | 久久久久国产精品 | 一区二区三区电影在线观看 | 亚洲视频在线看 | 免费看一区二区三区 | 国产精品99久久久久久宅男 | 日韩成人在线视频 | 精品成人免费一区二区在线播放 | 国产精品久久久久久久免费大片 | 综合色影院 | 国产精品亚洲第一区在线暖暖韩国 | 北条麻妃国产九九九精品小说 | 九九久久国产 | 免费看片在线播放 | 欧美日韩一区二区三区在线观看 | 久久一区二区三区四区 | 美女视频h | 国产日韩欧美一区二区 | 日韩毛片在线观看 | 精品视频一区二区在线观看 |