日韩在线不卡免费视频一区,日韩欧美精品一区二区三区经典,日产精品码2码三码四码区,人妻无码一区二区三区免费,日本feerbbwdh少妇丰满

Rust嵌入式
認(rèn)證:普通會員
作者動(dòng)態(tài)
這些單片機(jī)也能運(yùn)行 Rust 啦!
2天前
Rust 為什么比C安全,請看數(shù)據(jù)類型對比
1星期前
C 轉(zhuǎn) Rust 嵌入式需要了解的新概念
2星期前
Rust 嵌入式學(xué)習(xí)會遇到的挑戰(zhàn)
2星期前
GDB 調(diào)試 Rust 嵌入式程序-以國產(chǎn)單片機(jī)為例
2星期前

Rust 異步 —— 讓嵌入式編程更加簡單

FuturesRust中用于異步編程,類似JavaScriptpromise的原理,兩者都是async/await語句的基礎(chǔ),用戶可用它們用串行編程的方式實(shí)現(xiàn)異步的功能。

Futures在標(biāo)準(zhǔn)的std和嵌入式的nostd環(huán)境都有支持,使用方式一致,在std環(huán)境中,比較出名的有Tokio實(shí)現(xiàn)了異步的平臺,在嵌入式領(lǐng)域中,embassy也提供了高效的異步平臺。

到底什么是 Future?

簡單的說,Future用于表示一些異步計(jì)算的值,也就是說無法在當(dāng)前得出最終的結(jié)果,但由于串行的程序中,又需要該計(jì)算的結(jié)果用于后續(xù)的操作。舉個(gè)例子,在嵌入式中,通常處理串口接收和處理數(shù)據(jù)時(shí),采用串行編程的方式如下:

void loop() {
    char ch;
    if (ch == serial.read())
        switch ch {
            case 'A': do_someting(); break;
            case 'B':   do_someting_else(); break;
            ...

            default: break;
        }
    }
    do_someting();
}

在這個(gè)簡單的例程中,可以很容易看出處理的邏輯,但是CPU的執(zhí)行效率卻很低,CPU或進(jìn)程會阻塞在串口的讀接口中。也許更加有經(jīng)驗(yàn)的程序員會用中斷或操作系統(tǒng)來實(shí)現(xiàn)這個(gè)功能。但是需要加倍小心多線程或中斷引發(fā)的其他風(fēng)險(xiǎn),同時(shí)代碼的可閱讀性會降低,需要去了解操作系統(tǒng)和信號量等全局變量。如果使用RustFuture來替代該程序,則可簡單如下:

async fn loop() {
    let ch = serial.read().await;
    match ch {
        'A' => {
            do_someting();
        }
        'B' => {
            do_someting_else(); 
        }
        _ => {
            do_some();
        }
    }
    do_someting();
}

在異步的Rust代碼中,同樣保持了串行的編程模式,但CPU或線程不會在read()停留等待可讀數(shù)據(jù),而是在后臺數(shù)據(jù)來臨時(shí)自動(dòng)喚醒。這樣提高了運(yùn)行效率。

Future的實(shí)現(xiàn)原理

在大多數(shù)需要等待結(jié)束的任務(wù)中,系統(tǒng)后臺需要一個(gè)執(zhí)行器,通過喚醒Future的事件來重新激活await語句,簡單得說,需要執(zhí)行器對該任務(wù)實(shí)現(xiàn)任務(wù)和激活機(jī)制的抽象,該抽象無需反復(fù)去查詢事件信號,而任務(wù)是被動(dòng)讓激活信號重新喚醒。Future的簡單模型如下:

se std::cell::RefCell;

thread_local!(static NOTIFY: RefCell = RefCell::new(true));

struct Context<'a> {
    waker: &'a Waker,
}

impl<'a> Context<'a> {
    fn from_waker(waker: &'a Waker) -> Self {
        Context { waker }
    }

    fn waker(&self) -> &'a Waker {
        &self.waker
    }
}

struct Waker;

impl Waker {
    fn wake(&self) {
        NOTIFY.with(|f| *f.borrow_mut() = true)
    }
}

enum Poll {
    Ready(T),
    Pending,
}

trait Future {
    type Output;

    fn poll(&mut self, cx: &Context) -> Poll;
}

#[derive(Default)]
struct MyFuture {
    count: u32,
}

impl Future for MyFuture {
    type Output = i32;

    fn poll(&mut self, ctx: &Context) -> Poll {
        match self.count {
            3 => Poll::Ready(3),
            _ => {
                self.count += 1;
                ctx.waker().wake();
                Poll::Pending
            }
        }
    }
}

fn run(mut f: F) -> F::Output
where
    F: Future,
{
    NOTIFY.with(|n| loop {
        if *n.borrow() {
            *n.borrow_mut() = false;
            let ctx = Context::from_waker(&Waker);
            if let Poll::Ready(val) = f.poll(&ctx) {
                return val;
            }
        }
    })
}

fn main() {
    let my_future = MyFuture::default();
    /// 將輸出:Output: 3
    println!("Output: {}", run(my_future));
}

如上所示,run函數(shù)帶有一個(gè)Future屬性,可理解為調(diào)度器。在NOTIFY的信號中重新激活任務(wù),然后返回執(zhí)行結(jié)果,給Poll::Ready帶出。通常NOTIFYloop閉包至少會執(zhí)行一次,第一次是首次進(jìn)入poll任務(wù),如果poll任務(wù)后沒有結(jié)束,將會在Waker信號被激活時(shí)重新喚起執(zhí)行poll的任務(wù)。如果沒有喚醒事件,則調(diào)度器不會主動(dòng)去執(zhí)行Poll。Context用來傳遞任務(wù)的上下文,可用來保存當(dāng)前任務(wù)的事件信號等。Poll則是一個(gè)簡單的枚舉,Ready代表任務(wù)可以結(jié)束后的結(jié)果,將帶回結(jié)果返回值,Pending則代表當(dāng)前任務(wù)并未結(jié)束,則繼續(xù)睡眠。Futuretrait 則是任務(wù)的抽象,任何只要實(shí)現(xiàn)Future``trait,都可使用在異步編程中,即使用async/awit。在該例中根據(jù)原理實(shí)現(xiàn)了一個(gè)最簡單的Future的調(diào)度器,以及一個(gè)實(shí)現(xiàn)了Future的結(jié)構(gòu)體對象MyFuture。也許看起來要實(shí)現(xiàn)異步需要這么多代碼,似乎有點(diǎn)復(fù)雜!別擔(dān)心,這些Rust都已經(jīng)為您提供了,你需要寫的也就是main函數(shù)的內(nèi)容,甚至更加簡單。下面展示embassy使用異步的方式處理串口數(shù)據(jù)。

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_nrf::init(Default::default());
    let mut config = uarte::Config::default();
    config.parity = uarte::Parity::EXCLUDED;
    config.baudrate = uarte::Baudrate::BAUD115200;

    let mut uart = uarte::Uarte::new(p.UARTE0, Irqs, p.P0_08, p.P0_06, config);

    info!("uarte initialized!");

    // Message must be in SRAM
    let mut buf = [0; 8];
    buf.copy_from_slice(b"Hello!\r\n");

    unwrap!(uart.write(&buf).await);
    info!("wrote hello in uart!");

    loop {
        info!("reading...");
        unwrap!(uart.read(&mut buf).await);
        info!("writing...");
        unwrap!(uart.write(&buf).await);
    }
}

最后

Rust中使用Future是零成本抽象的,即不會生成多余的狀態(tài)邏輯代碼,同時(shí)CPU的運(yùn)行也不會造成負(fù)荷,同時(shí)代碼的可閱讀性依舊很強(qiáng)。如果有興趣可以深入閱讀以下書籍:

  • Asynchronous Programming in Rust:https://rust-lang.github.io/async-book/02_execution/02_future.html
  • Async programming in Rust with async-std:https://book.async.rs/concepts/futures
  • Async Rust:https://www.oreilly.com/library/view/async-rust/9781098149086/
聲明:本內(nèi)容為作者獨(dú)立觀點(diǎn),不代表電子星球立場。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請聯(lián)系:editor@netbroad.com
覺得內(nèi)容不錯(cuò)的朋友,別忘了一鍵三連哦!
贊 2
收藏 3
關(guān)注 14
成為作者 賺取收益
全部留言
0/200
成為第一個(gè)和作者交流的人吧