文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
2. 任务
任务 也被称作绿色线程(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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论