翻译进度
6
分块数量
1
参与人数

2.2.Tasks

这是一篇协同翻译的文章,你可以点击『我来翻译』按钮来参与翻译。

原文链接:book.async.rs/


Tasks

既然已经了解了 Futures 是怎么一回事儿,下面一起来运行一下它们!

async-std 中, 模块 [tasks][tasks] 起此作用。 最简单的方法是使用 block_on 函数:

# extern crate async_std;
use async_std::{fs::File, io, prelude::*, task};

// 读文件 异步方法
async fn read_file(path: &str) -> io::Result<String> {
    let mut file = File::open(path).await?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).await?;
    Ok(contents)
}

fn main() {
    let reader_task = task::spawn(async {
        let result = read_file("data.csv").await;
        match result {
            Ok(s) => println!("{}", s),
            Err(e) => println!("Error reading file: {:?}", e)
        }
    });
    println!("Started task!");
    task::block_on(reader_task);
    println!("Stopped task!");
}

上述代码的逻辑是将读文件的代码托管给 async_std 运行时去执行。让我们从内到外的一步步深入执行逻辑。

# extern crate async_std;
# use async_std::{fs::File, io, prelude::*, task};
#
# async fn read_file(path: &str) -> io::Result<String> {
#     let mut file = File::open(path).await?;
#     let mut contents = String::new();
#     file.read_to_string(&mut contents).await?;
#     Ok(contents)
# }
#
async {
    let result = read_file("data.csv").await;
    match result {
        Ok(s) => println!("{}", s),
        Err(e) => println!("Error reading file: {:?}", e)
    }
};

这是一个 async 代码块。调用 async 函数就少不了 async 代码块,并且 async 代码块向编译器标识在执行时需要包含进入执行过程的所有相关的指令。在 Rust 中,所有的代码块都要有返回值,而 async 代码块返回的值的类型是 Future

AllenXu9527 翻译于 3年前

接下来,让我们开启有趣的部分:

# extern crate async_std;
# use async_std::task;
task::spawn(async { });

spawn 使用了 Future,开启一个 Task 运行程序,返回 JoinHandle。Rust 中的 Futures 有时被称作 Futures。 意思是你需要一些辅助信息来帮助运行。为了运行 Future,可能需要一些额外的簿记,例如它的运行状态是正在运行还是已完成,它在内存中的位置,以及当前状态是什么。该簿记部分在 Task 中被抽象。

TaskThread 相似,但又有一些细微差别:Task 由程序调度,而不是操作系统内核调度,并且在需要等待的地方,等待结束后程序本身负责再次唤醒它。这点稍后再讨论。一个 async_std 任务也可以有名称和 ID ,就像线程一样。

现在,只需要知道 spawned 一个任务,该任务就可以继续在后台运行。JoinHandle 本身就是一个 future ,一旦 Task 结束,它就会结束。与 threadsjoin 函数非常相似,我们现在可以通过句柄调用 block_on阻塞程序 (或者具体的说另启动一个线程) ,然后等待它完成。

AllenXu9527 翻译于 3年前

async_std 中的 Task

Task 是 async_std 的核心抽象之一。 和 Rust 的 thread 一样,Task 提供了一些原始概念上的实用功能。 Task 与运行时有关, 同时它们本身又是独立的。 async_std 的 task 有许多理想的属性:

  • 所有分配一次完成
  • 所有任务都有 backchannel(回发通道),通过 JoinHandle 将结果和错误回传到生成任务
  • 带有用于调试的元数据
  • 支持本地存储任务

async_std 的 task API 可以处理后台运行时的设置和拆除,不用依赖于显式启动的运行时。

AllenXu9527 翻译于 3年前

阻塞

假定 Task 是并发运行,那么可能是通过共享执行线程来处理并发的。这意味着阻塞进行中的系统线程的操作,例如

std::thread::sleep

或调用 Rust 的 std 类库的 io 函数,都将停止执行共享此线程的所有任务。其他的库(例如数据库驱动程序)就有类似的行为。 需注意,阻塞当前线程本身并不是不好的行为,只是其不能与 async-std 的并发执行模型不能很好地混合使用。本质上,永远不要有以下操作:

# extern crate async_std;
# use async_std::task;
fn main() {
    task::block_on(async {
        // this is std::fs, which blocks
        std::fs::read_to_string("test_file");
    })
}

如果要多种混合操作,请考虑将此类阻塞操作放在单独的 thread 上。

AllenXu9527 翻译于 3年前

Errors and panics

Tasks report errors through normal patterns: If they are fallible, their Output should be of kind Result<T,E>.

In case of panic, behaviour differs depending on whether there's a reasonable part that addresses the panic. If not, the program aborts.

In practice, that means that block_on propagates panics to the blocking component:

# extern crate async_std;
# use async_std::task;
fn main() {
    task::block_on(async {
        panic!("test");
    });
}
thread 'async-task-driver' panicked at 'test', examples/panic.rs:8:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

While panicing a spawned task will abort:

# extern crate async_std;
# use async_std::task;
# use std::time::Duration;
task::spawn(async {
    panic!("test");
});

task::block_on(async {
    task::sleep(Duration::from_millis(10000)).await;
})
thread 'async-task-driver' panicked at 'test', examples/panic.rs:8:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
Aborted (core dumped)

That might seem odd at first, but the other option would be to silently ignore panics in spawned tasks. The current behaviour can be changed by catching panics in the spawned task and reacting with custom behaviour. This gives users the choice of panic handling strategy.

结论

async_std 附带了一个有用的 Task 类型,它与类似 std::thread 的 API 配合使用。它以结构化和定义的方式涵盖了错误行为和紧急情况。

任务是独立的并发单元,有时它们需要通信。这就是 Stream 的来源。

AllenXu9527 翻译于 4年前

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~