use futures::future::{BoxFuture, Future, join_all};
use tokio::sync::Mutex;
use tokio::time::{self, Duration};
#[macro_use]
extern crate lazy_static;
use std::sync::Arc;
async fn a_job() {
std::thread::sleep(Duration::from_secs(1));
println!("I am working with a_job!");
}
async fn b_job() {
std::thread::sleep(Duration::from_secs(10));
println!("I am working with b_job!");
}
struct Job<R> {
func: Box<dyn Fn() -> BoxFuture<'static, R> + Send + Sync + 'static>,
}
impl<R> Job<R> {
fn new<F>(f: fn() -> F) -> Job<F::Output>
where
F: Future<Output = R> + Send + 'static,
{
Job {
func: Box::new(move || Box::pin(f())),
}
}
async fn run(&self) -> R {
(self.func)().await
}
}
lazy_static! {
static ref GLOBAL: Arc<Mutex<Vec<Job<()>>>> = Arc::new(Mutex::new(vec![Job::new(a_job), Job::new(b_job)]));
}
#[tokio::main]
async fn main() {
let recv_trigger = tokio::spawn(async move {
let mut interval = time::interval(time::Duration::from_secs(1));
loop {
tokio::select! {
_ = interval.tick() => {
let global = GLOBAL.clone();
let mut v = global.lock().await;
let mut tasks = Vec::new();
for job in v.iter_mut(){
tasks.push(job.run());
}
join_all(tasks).await;
}
}
}
});
let _ = tokio::join!(recv_trigger);
}
I encounter a problem, when the interval.tick() is true, the b_job and a_job wait 10 seconds synchronously, but I want b_job and a_job to wait in background, and print:
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with a_job!
I am working with b_job!
I am working with a_job!
.....
it just is similar to go keyword in golang, so how to implement it?
If you use join_all on futures, they are executed in a sort of “cooperative multitasking” manner on the same thread. Multi-threading with async code execution happens, too, on a per-task basis when you use tokio::spawn to spawn tasks; even then the parallelization is limited to a fixed set of worker thread. In either case, you need to ensure that no task is unnecessarily blocking the execution, that’s the “cooperative” part of “cooperative multitasking”: bad players can starve the whole system.
Now, std::thread::sleepis a blocking function call; a “bad player” so to speak. Or put differently: You’re using a synchronous sleep primitive (thread::sleep) in an asynchronous environment (async fns, executed on tokio’s executor). To fix the problem, you’ll need to replace
Sorry, I didn't mean that , I mean, is there any way to run task and wait in background? When the interval is activated, a_job and b_job run and wait task in background, after a one-second delay,interval is activated again without waiting for threads spawned by a_job and b_job finish, it is just like golang: go a_job().
As mentioned, you can run tasks in the background with tokio::spawn. Your functions a_job and b_job are terrible functions because they’re async fn, yet block execution synchronously for multiple seconds. If these things are stand-ins for something else, there’s three possibilites I can come up with: Either they are computation-intensive jobs, in which case they should not be async fns, and tokio might be the wrong framework for executing those; but you could try using tokio::spawn_blocking in order to execute them in a large pool of allowed-to-be-blocking worker threads; or they’re handling some form of IO synchronously, in which case you should consider making them asynchronous instead (in this example the mentioned transition from std::tread to tokio::time) or you could turn them into non-async functions and use spawn_blocking as-well (usually less efficient and less scalable), or they’re already asynchronous tasks, (they aren’t in your example where they use thread::sleep!), in which case you can use tokio::spawn to start them in the background. With tokio::spawn, you don’t need to keep and await the returned handle, if you’re not interested in observing the result/completion of the spawned task. You can just drop the handle and it will keep running in the background.