我想使用Rust创建一个简单的调度程序,以便在定义的时间运行多个并发函数,但是如果尚未完成,则不要启动更多的
。例如,如果定义的间隔为一秒,则调度程序应运行函数,并且如果未返回先前的函数,则不要启动更多函数。目的是防止多次运行同一功能。
我用Go这样创建a working example:
package main
import (
"fmt"
"sync"
"time"
)
func myFunc(wg *sync.WaitGroup) {
fmt.Printf("now: %+s\n", time.Now())
time.Sleep(3 * time.Second)
wg.Done()
}
func main() {
quit := make(chan bool)
t := time.NewTicker(time.Second)
go func() {
for {
select {
case <-t.C:
var wg sync.WaitGroup
for i := 0; i <= 4; i++ {
wg.Add(1)
go myFunc(&wg)
}
wg.Wait()
fmt.Printf("--- done ---\n\n")
case <-quit:
return
}
}
}()
<-time.After(time.Minute)
close(quit)
}
由于我在Rust标准库中找不到类似Go的NewTicker
之类的东西,因此我使用了Tokio和came up with this
extern crate futures;
extern crate tokio;
use futures::future::lazy;
use std::{thread, time};
use tokio::prelude::*;
use tokio::timer::Interval;
fn main() {
let task = Interval::new(time::Instant::now(), time::Duration::new(1, 0))
.for_each(|interval| {
println!("Interval: {:?}", interval);
for i in 0..5 {
tokio::spawn(lazy(move || {
println!("I am i: {}", i);
thread::sleep(time::Duration::from_secs(3));
Ok(())
}));
}
Ok(())
})
.map_err(|e| panic!("interval errored; err={:?}", e));
tokio::run(task);
}
这种方法的问题在于任务不等待调用先前的函数,因此无论先前是否运行这些函数,它们都会再次启动,我在这里缺少像Go的sync.WaitGroup
之类的东西。可以使用什么来获得与工作示例相同的结果?
是否可以仅通过使用标准库来实现?这主要是出于学习目的,可能有一种非常简单的方法可以做到,我可以避免额外的麻烦。
最后,我想通过HTTP定期监视某些站点(仅获取返回的状态代码),但是在获得所有响应之前,不要再次查询所有站点。
答案 0 :(得分:1)
由于您希望并发并且仅使用标准库,因此您基本上必须使用线程。
在这里,我们为调度程序循环的每次迭代的每个函数启动一个线程,从而使它们可以并行运行。然后,我们等待所有功能完成,以防止同时运行同一功能两次。
use std::{
thread,
time::{Duration, Instant},
};
fn main() {
let scheduler = thread::spawn(|| {
let wait_time = Duration::from_millis(500);
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
let start = Instant::now();
eprintln!("Scheduler starting at {:?}", start);
let thread_a = thread::spawn(a);
let thread_b = thread::spawn(b);
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
let runtime = start.elapsed();
if let Some(remaining) = wait_time.checked_sub(runtime) {
eprintln!(
"schedule slice has time left over; sleeping for {:?}",
remaining
);
thread::sleep(remaining);
}
}
});
scheduler.join().expect("Scheduler panicked");
}
fn a() {
eprintln!("a");
thread::sleep(Duration::from_millis(100))
}
fn b() {
eprintln!("b");
thread::sleep(Duration::from_millis(200))
}
您还可以使用Barrier
在线程中启动每个函数一次,然后在执行结束时将它们同步:
use std::{
sync::{Arc, Barrier},
thread,
time::Duration,
};
fn main() {
let scheduler = thread::spawn(|| {
let barrier = Arc::new(Barrier::new(2));
fn with_barrier(barrier: Arc<Barrier>, f: impl Fn()) -> impl Fn() {
move || {
// Make this an infinite loop
// Or some control path to exit the loop
for _ in 0..5 {
f();
barrier.wait();
}
}
}
let thread_a = thread::spawn(with_barrier(barrier.clone(), a));
let thread_b = thread::spawn(with_barrier(barrier.clone(), b));
thread_a.join().expect("Thread A panicked");
thread_b.join().expect("Thread B panicked");
});
scheduler.join().expect("Scheduler panicked");
}
fn a() {
eprintln!("a");
thread::sleep(Duration::from_millis(100))
}
fn b() {
eprintln!("b");
thread::sleep(Duration::from_millis(200))
}
我个人不会使用这些解决方案中的任何一个。我会find a crate在其他人编写和测试我需要的代码的地方。
另请参阅: