是否可以在没有互斥锁的情况下从Fn调用FnOnce?

时间:2018-03-07 00:55:38

标签: rust

Fn可以通过渠道发送,但FnOnce尚未稳定。要通过渠道发送FnOnce,可以将其打包在Fn中,如下所示。

但是,这需要一个Mutex,这会在扩展到非常高的吞吐量时引入恼人的开销(即,实际上,当你这么做时,它很慢)。

我是否可以使用其他一些不太重量级的并发原语?或许std::sync::atomic?这可以在没有锁定的情况下完成吗?

我没有兴趣使用依赖于未定义行为的夜间特征或板条箱。

use std::thread;
use std::sync::Mutex;
use std::sync::mpsc;

struct RawFunc {
    data: Box<Fn() + Send + 'static>,
}

impl RawFunc {
    fn new<T>(inner: T) -> RawFunc
    where
        T: FnOnce() + Send + 'static,
    {
        let inner_lock = Mutex::new(Some(inner));
        return RawFunc {
            data: Box::new(move || match inner_lock.lock() {
                Ok(mut i) => (i.take().unwrap())(),
                Err(_) => {}
            }),
        };
    }

    fn invoke(self) {
        (self.data)()
    }
}

fn main() {
    // Local
    let x = RawFunc::new(move || {
        println!("Hello world");
    });
    x.invoke();

    // Via channel
    let (sx, rx) = mpsc::channel::<RawFunc>();
    sx.send(RawFunc::new(move || {
        println!("Hello world 2");
    })).unwrap();
    let output = rx.recv().unwrap();
    output.invoke();

    // In a thread
    let guard = thread::spawn(move || {
        let output = rx.recv().unwrap();
        output.invoke();
    });

    sx.send(RawFunc::new(move || {
        println!("Hello world 3!");
    })).unwrap();

    guard.join().unwrap();

    // Passing arbitrary data to a thread
    let (sx, rx) = mpsc::channel::<RawFunc>();
    let guard = thread::spawn(move || {
        let output = rx.recv().unwrap();
        output.invoke();
    });

    let bar = RawFunc::new(move || {
        println!("Moved func!");
    });
    let foo = String::from("Hello World 4");
    sx.send(RawFunc::new(move || {
        println!("Some moved data: {:?}", foo);
        bar.invoke();
    })).unwrap();

    guard.join().unwrap();
}

playground

1 个答案:

答案 0 :(得分:3)

cursive crate的作者有完全相同的问题,并以自己的特点解决了这个问题。

/// Asynchronous callback function trait.
///
/// Every `FnOnce(&mut Cursive) -> () + Send` automatically
/// implements this.
///
/// This is a workaround only because `Box<FnOnce()>` is not
/// working and `FnBox` is unstable.
pub trait CbFunc: Send {
    /// Calls the function.
    fn call_box(self: Box<Self>, &mut Cursive);
}

impl<F: FnOnce(&mut Cursive) -> () + Send> CbFunc for F {
    fn call_box(self: Box<Self>, siv: &mut Cursive) {
        (*self)(siv)
    }
}

source

Here is the PR,代码介绍。