如何不按标准间隔运行一组功能,而又仅使用标准Rust库运行同一功能?

时间:2019-05-22 09:31:33

标签: rust

我想使用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之类的东西,因此我使用了Tokiocame 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定期监视某些站点(仅获取返回的状态代码),但是在获得所有响应之前,不要再次查询所有站点。

1 个答案:

答案 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在其他人编写和测试我需要的代码的地方。

另请参阅: