返回介绍

2. 任务

发布于 2024-10-13 11:25:33 字数 4554 浏览 0 评论 0 收藏 0

任务 也被称作绿色线程(asynchronous green thread),由调度器工作线程(worker thead)并发执行。

  • 轻量级,所需内存极少,可生成大量并发任务。
  • 如果运行时关闭,则无法确保任务被执行。
  • 协作式调度,需要异步实现配合。
  • 任务可跨线程执行。(多线程模式)
  • 任务不能直接引用外部数据。


通过 spawn 创建并立即执行任务,返回 JoinHandler 等待结果。

use tokio::task::spawn;

async fn test() -> i64 {
  100
}

#[tokio::main]
async fn main() {
  let h = spawn(test());
  assert_eq!(h.await.unwrap(), 100);
}
use tokio::task::spawn;

#[tokio::main]
async fn main() {
  let h = spawn(async{
    100
  });
  assert_eq!(h.await.unwrap(), 100);
}

需要注意,创建任务后,即便没有 JoinHandle.await ,它也会执行(运行时未关闭)。

use tokio::time::{sleep, Duration};
use tokio::task::spawn;

#[tokio::main]
async fn main() {
  let h = spawn(async{
    println!("task");
  });

  sleep(Duration::from_secs(1)).await;
  println!("done");
  
  // _ = h.await;
}

// task
// done
use tokio::time::{sleep, Duration};
use tokio::task::spawn;

#[tokio::main]
async fn main() {
  let h = spawn(async{
    sleep(Duration::from_secs(1)).await;
    println!("task");
  });

  println!("done");
  
  // 等待任务结束。
  _ = h.await;
}

// done
// task

不能直接引用外部数据,因为编译器无法在多线程间协调。

use tokio::task::spawn;

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

  spawn(async {
    println!("{:?}", v);
  });
}

/*

error[E0373]: async block may outlive the current function, 
but it borrows `v`, which is owned by the current function

   |
 7 |     spawn(async {
   |  _________________^
 8 | |     println!("{:?}", v);
   | |              - `v` is borrowed here
 9 | |   });
   | |_____^ may outlive borrowed value `v`
   |
   = note: async blocks are not executed immediately and must either
       take a reference or ownership of outside variables they use
       
help: to force the async block to take ownership of `v` (and any 
    other referenced variables), use the `move` keyword
   |
 7 |   spawn(async move {
   |         ++++

*/

解决方法是,以 async move 转移,或用 Arc(Mutex) 共享所有权。

use std::sync::{Arc, Mutex};
use tokio::task::spawn;

#[tokio::main]
async fn main() {
  let v = Arc::new(Mutex::new(vec![1, 2, 3]));
  let c = Arc::clone(&v);
  
  spawn(async move {           // move !!!
    let mut c = c.lock().unwrap();
    c.push(4);
    println!("{:?}", c);
  });
}

// [1, 2, 3, 4]

如任务内有 await ,恢复时未必是原线程。强制要求实现 Send ,确保跨线程传送安全。
当然,仅在 await 前用 !Send 没有问题,也可用 spawn_local 在同一线程完成。

use std::rc::Rc;
use tokio::task::{spawn, yield_now};

#[tokio::main]
async fn main() {
  spawn(async {
    let rc = Rc::new(0);   // !Send

    yield_now().await;
    
    println!("{:?}", rc);
  });
}

/*

error: future cannot be sent between threads safely
  |
6   |   spawn(async {
  |   ^^^^^ future created by async block is not `Send`
  |
  = help: within `impl Future<Output = ()>`, the trait `Send` 
      is not implemented for `Rc<i32>`
      
note: future is not `Send` as this value is used across an await
  |
7   |     let rc = Rc::new(0);
  |       -- has type `Rc<i32>` which is not `Send`
  | 
9   |     yield_now().await;
  |          ^^^^^^ await occurs here, with `rc` maybe used later
...
  |   });
  |   - `rc` is later dropped here

*/
#[tokio::main]
async fn main() {
  spawn(async {
    {
      let rc = Rc::new(0);   // !Send
      println!("{:?}", rc);
    }              // drop! 无需保存状态。
    
    yield_now().await;    
  });
}

其他函数:

  • spawn_blocking :在运行时外的专用线程池内执行阻塞任务。
  • block_in_place :将当前线程转换为阻塞线程,所属其他任务转移到其他工作线程。
  • yield_now :挂起当前任务,让线程执行其他任务。后再次被调度执行。

join!

等待多个任务执行结束。

注意,在同一线程(same thread)内并发,与 spawn 多线程并行不同。

use std::time::{Instant, Duration};
use std::thread::sleep;
use tokio::join;

async fn a() -> i64 {
  sleep(Duration::from_secs(2));  // block
  1
}

async fn b() -> i64 {
  sleep(Duration::from_secs(2));
  2
}

#[tokio::main]
async fn main() {
  let now = Instant::now();
  println!("{:?}", join!(a(), b()));
  println!("{:?}", now.elapsed().as_secs());
}

// (1, 2)
// 4
use tokio::task::spawn;

#[tokio::main]
async fn main() {
  let now = Instant::now();

  let ha = spawn(a());
  let hb = spawn(b());

  println!("{:?}", join!(ha, hb));
  println!("{:?}", now.elapsed().as_secs());
}

// (Ok(1), Ok(2))
// 2

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文