如何在不等待的情况下轮询未来状态?

时间:2017-01-23 16:09:38

标签: asynchronous rust future

我尝试使用Future.rs在单独的进程中管理某些任务。我看到如何等待每个创造的未来以及如何一个接一个地处理它们,但是我无法在执行期间轮询未来以了解它的状态。我总是有错误:

  

线程'主要'惊慌失措没有任务正在运行'

我希望在将来的处理过程中做一些事情,直到它完成。也许我没有以正确的方式使用它?我设法通过使用频道使其工作,但我认为应该可以轮询未来以及何时准备好获得结果。 我用来测试它的代码是:

fn main() {
    println!("test future");
    let thread_pool = CpuPool::new(4);
    let mut future_execution_list = vec![];
    let mutex = Arc::new(AtomicUsize::new(0));
    //create the future to process
    for _ in 0..10 {
        let send_mutex = mutex.clone();
        let future = thread_pool.spawn_fn(move || {
            //Simulate long processing
            thread::sleep(time::Duration::from_millis(10));
            let num = send_mutex.load(Ordering::Relaxed);
            send_mutex.store(num + 1, Ordering::Relaxed);
            let res: Result<usize, ()> = Ok(num);
            res

        });
        future_execution_list.push(future);
    }
    // do the job
    loop {
        for future in &mut future_execution_list {
            match future.poll() {
                Ok(Async::NotReady) => (), //do nothing
                Ok(Async::Ready(num)) => {
                    //update task status
                    println!(" future {:?}", num);
                }
                Err(_) => {
                    //log error and set task status to err
                    ()
                }
            };
        }
        //do something else
    }
}

所以在Shepmaster回答之后我完成了我的问题。你的言论非常有趣,但我仍然无法找到解决问题的办法。我将添加一些关于我的问题的信息。我想在一个可以一次管理多个任务的自动化上安排任务。存在一个循环,其中管理事件并计算任务调度。当任务被安排时,它会产生。当任务结束时,完成新的调度。在任务执行期间,管理事件。一个speudo代码可以是:

loop {
   event.try_recv() { ...} //manage user command for exemple
   if (schedule) {
      let tasks_to_spawn = schedule_task();
      let futures = tasks_to_spawn.map(|task| {
           thread_pool.spawn_fn( ....)}); 
      let mut one = future::select_all(futures);
      while let Ok((value, _idx, remaining)) = one.wait() {..} //wait here
   }
   //depend on task end state and event  set schedule to true or false.

}

我可以在未来联合调度和任务,如:

let future = schedule.and_them(|task| execute_task);

但是我仍然需要等待第一个任务的执行结束。 我可以把所有事情都放在未来(事件管理,日程安排,任务),然后等待你提出的第一个结束。我尝试但我没有看到如何使用不同的项目和错误类型制作未来的vec。有了这个概念,我必须在线程之间管理更多数据。事件管理和调度不必在不同的线程中执行。

我看到另一个问题,select_all取得了vec的所有权。如果在执行另一个任务期间必须对新任务进行调整,我该如何更改vec并添加新的未来?

不知道你是否有一个简单的解决方案。我认为在使用isDone()之类的方法执行期间获取未来状态很简单,而不必等待。也许是它的计划,我没有看到关于那个的公关。 如果你有一个简单的解决方案,那将是很好的,否则我会重新考虑我的观念。

2 个答案:

答案 0 :(得分:2)

要轮询Future,您必须拥有Task。要获得Task,您可以在传递给Future的{​​{1}}中进行投票。如果你像这样重写你的例子的futures::executor::spawn()

loop

它运行。

至于为什么 futures::executor::spawn(futures::lazy(|| { // existing loop goes here })).wait_future(); 只能在任务中进行轮询,我相信轮询可以调用Future

答案 1 :(得分:0)

  

我想在将来的处理中做点什么

据我了解,那就是期货 - 可以并行发生的事情。如果你想做别的事情,那就再做一个未来并把它扔进去!

你基本上已经这样做了 - 你的每个主题都是&#34;做其他事情&#34;。

  

调查未来以及何时准备好获得结果

使用future::select_all,您可以组合多个期货,并获得最先完成的任何结果。然后由您决定等待下一个。

一种可能的实施方式:

extern crate rand;
extern crate futures;
extern crate futures_cpupool;

use rand::Rng;
use futures::{future, Future};
use futures_cpupool::CpuPool;

use std::{thread, time};

fn main() {
    let thread_pool = CpuPool::new(4);

    let futures = (0..10).map(|i| {
        thread_pool.spawn_fn(move || -> Result<usize, ()> {
            let mut rng = rand::thread_rng();
            // Simulate long processing
            let sleep_time = rng.gen_range(10, 100);
            let sleep_time = time::Duration::from_millis(sleep_time);
            for _ in 0..10 {
                println!("Thread {} sleeping", i);
                thread::sleep(sleep_time);
            }
            Ok(i)
        })
    });

    let mut one = future::select_all(futures);
    while let Ok((value, _idx, remaining)) = one.wait() {
        println!("Future #{} finished", value);
        if remaining.is_empty() {
            break;
        }
        one = future::select_all(remaining);
    }
}

在致电wait期间,同时发生了多项事情!这可以通过交错输出看出:

Thread 2 sleeping
Thread 0 sleeping
Thread 3 sleeping
Thread 1 sleeping
Thread 3 sleeping
Thread 0 sleeping
Thread 1 sleeping
Thread 2 sleeping
Thread 3 sleeping

您可以通过将每个线程的休眠时间设置为1秒并对整个程序进行计时来验证事情是否并行发生。由于有10个期货,需要1秒,平行度为4,整个程序需要3秒才能运行。

奖金代码审核:

  1. 不要拆分加载并设置原子变量来实现递增 - 存储的值可能已被两次调用之间的另一个线程更改。使用fetch_add
  2. 在使用它们之前,您确实应该知道what the orderings mean。我不知道他们,所以我总是使用SeqCst
  3. 由于这个例子并不重要,我完全删除了原子变量。
  4. 总是喜欢收集到Vec而不是在循环内推入它。这样可以实现更优化的分配。
  5. 在这种情况下,由于Vec接受任何可以转换为迭代器的内容,因此根本不需要select_all