简体   繁体   English

有一个 rust function 暂停执行并多次返回值而不丢失其堆栈

[英]Have a rust function pause execution and return a value multiple times in without losing its stack

I am working on a project that has long compute times to which I will have hundreds of nodes running it, as part of my implementation I have a status handler object/struct which talks to the API and gets the needed information like the arguments, the status handler then calls the main intensive function.我正在从事一个计算时间很长的项目,我将有数百个节点运行它,作为我实现的一部分,我有一个状态处理程序对象/结构,它与 API 对话并获取所需的信息,如 arguments,状态处理程序然后调用主密集 function。

It order to keep tabs on the computationally intensive function I would like it to yield back to the status handler function with the completed percentage so the status handler can update the API and then allow the intensive function to continue computation without losing any of its stack (such as it variables and file handles)它为了密切关注计算密集型 function 我希望它以完成的百分比返回状态处理程序 function 以便状态处理程序可以更新 API 然后允许密集型 function 继续计算而不会丢失任何堆栈(例如 it 变量和文件句柄)

I've looked into async function but they seem to only return once.我查看了 async function 但它们似乎只返回一次。

Thank you in advance!先感谢您!

Async can in fact pause and resume, but it is meant for IO bound programs that basically wait for some external IO the entire time.异步实际上可以暂停和恢复,但它适用于 IO 绑定程序,这些程序基本上一直在等待一些外部 IO。 It's not meant for computation heavy tasks .它不适用于计算繁重的任务

There two ways that come to my mind on how to solve this problem:关于如何解决这个问题,我想到了两种方法:

  • threads & channels线程和通道
  • callbacks回调

Solution 1: Threads & Channels解决方案 1:线程和通道

use std::{sync::mpsc, thread, time::Duration};

struct StatusUpdate {
    task_id: i32,
    percent: f32,
}
impl StatusUpdate {
    pub fn new(task_id: i32, percent: f32) -> Self {
        Self { task_id, percent }
    }
}

fn expensive_computation(id: i32, status_update: mpsc::Sender<StatusUpdate>) {
    status_update.send(StatusUpdate::new(id, 0.0)).unwrap();
    thread::sleep(Duration::from_millis(1000));
    status_update.send(StatusUpdate::new(id, 33.3)).unwrap();
    thread::sleep(Duration::from_millis(1000));
    status_update.send(StatusUpdate::new(id, 66.6)).unwrap();
    thread::sleep(Duration::from_millis(1000));
    status_update.send(StatusUpdate::new(id, 100.0)).unwrap();
}

fn main() {
    let (status_sender_1, status_receiver) = mpsc::channel();
    let status_sender_2 = status_sender_1.clone();

    thread::spawn(move || expensive_computation(1, status_sender_1));
    thread::spawn(move || expensive_computation(2, status_sender_2));

    for status_update in status_receiver {
        println!(
            "Task {} is done {} %",
            status_update.task_id, status_update.percent
        );
    }
}
Task 1 is done 0 %
Task 2 is done 0 %
Task 1 is done 33.3 %
Task 2 is done 33.3 %
Task 1 is done 66.6 %
Task 2 is done 66.6 %
Task 2 is done 100 %
Task 1 is done 100 %

Solution 2: Callbacks解决方案 2:回调

use std::{thread, time::Duration};

struct StatusUpdate {
    task_id: i32,
    percent: f32,
}
impl StatusUpdate {
    pub fn new(task_id: i32, percent: f32) -> Self {
        Self { task_id, percent }
    }
}

fn expensive_computation<F: FnMut(StatusUpdate)>(id: i32, mut update_status: F) {
    update_status(StatusUpdate::new(id, 0.0));
    thread::sleep(Duration::from_millis(1000));
    update_status(StatusUpdate::new(id, 33.3));
    thread::sleep(Duration::from_millis(1000));
    update_status(StatusUpdate::new(id, 66.6));
    thread::sleep(Duration::from_millis(1000));
    update_status(StatusUpdate::new(id, 100.0));
}

fn main() {
    expensive_computation(1, |status_update| {
        println!(
            "Task {} is done {} %",
            status_update.task_id, status_update.percent
        );
    });
}
Task 1 is done 0 %
Task 1 is done 33.3 %
Task 1 is done 66.6 %
Task 1 is done 100 %

Note that with the channels solution it is much easier to handle multiple computations on different threads at once.请注意,使用通道解决方案可以更轻松地同时处理不同线程上的多个计算。 With callbacks, communicating between threads is hard/impossible.使用回调,线程之间的通信很难/不可能。

am I able to pause execution of the expensive function while I do something with that data then allow it to resume?我是否可以暂停执行昂贵的 function 而我对该数据执行某些操作然后允许它恢复?

No, this is not something that can be done with threads.不,这不是线程可以完成的事情。 In general.一般来说。

You can run them with a lower priority than the main thread, which means they won't be scheduled as aggressively, which decreases the latency in the main thread.您可以以低于主线程的优先级运行它们,这意味着它们不会被积极调度,从而减少主线程中的延迟。 But in general, operating systems are preemptive and are capable of switching back and forth between threads, so you shouldn't worry about 'pausing'.但一般来说,操作系统是抢占式的,能够在线程之间来回切换,所以你不必担心“暂停”。

What you are asking for are generators , which are currently not stable.您要的是generators ,目前不稳定。 You can either experiment with them on nightly , or manually create something that works like them and manually call it (though it won't has as nice syntax as generators).您可以在nightly上试验它们,或者手动创建像它们一样工作的东西并手动调用它(尽管它的语法不如生成器)。 Something like this for example:例如这样的事情:

enum Status<T, K> {
    Updated(T),
    Finished(K)
}

struct State { /* ... */ }

impl State {
    pub fn new() -> Self {
        Self { /* ... */ }
    }
    
    pub fn call(self) -> Status<Self, ()> {
        // Do some update on State and return
        // State::Updated(self). When you finised
        // return State::Finished(()). Note that this
        // method consumes self. You could make it take
        // &mut self, but then you would have to worry
        // about how to prevent it beeing called after
        // it finished. If you want to return some intermidiate
        // value in each step you can make Status::Updated
        // contain a (state, value) instead.
        
        todo!()
    }
}

fn foo() {
    let mut state = State::new();
    
    let result = loop {
        match state.call() {
            Status::Updated(s) => state = s,
            Status::Finished(result) => break result
        }
    };
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 暂停执行代码,直到异步函数返回所需值 - Pause execution of code until Asynchronous function returns the desired value 如何使用承诺执行函数并通过 API 调用多次检查其值并使用该值执行另一个函数? - How can I execute a function with promises and check its value multiple times by API call and execute another function with the value? 多次调用函数而无需等待它完成 - Call a function multiple times without waiting for it to complete 计算多次调用的异步函数的执行时间 - calculate execution times of async function over multiple calls 我可以暂停执行功能,直到在节点中收到一些值吗? - Can I pause the execution of function until some value has been received in node? 有没有更简单的方法来暂停执行然后在异步 function 上进行回调? - Is there an easier way to pause execution then a callback on an async function? REST-Endpoint:没有返回值的异步执行 - REST-Endpoint: Async execution without return value 模拟函数的返回值没有`then`属性 - Return value of a mocked function does not have `then` property 多次运行函数,而无需等待php中的响应 - run a function multiple times without waiting for response in php 从其父级的异步函数返回值 - Return value from async function from its parent
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM