防止在Fn已经运行时再次调用它

时间:2018-01-25 05:26:09

标签: concurrency macros rust keyboard-shortcuts

我正在使用inputbot编写一个程序,为我的计算机提供一些全局宏。例如,当我按 h 键时,它应该执行宏输入

  

Hello World

进入当前的应用程序。我试图像这样实现它:

extern crate inputbot;

fn main() {
    let mut callback = || {
        inputbot::KeySequence("Hello World").send();
    };

    inputbot::KeybdKey::HKey.bind(callback);

    inputbot::handle_input_events();
}

然而,当我按下 h 键时,实际得到的是:

  

hHHHHHHHHHHHHHHHHHHHHHHHHHEHEHhEhEEHHhEhEhEHhEHHEHHEEHhEHlhEHEHHEHLEHLHeeleleelelelllelelleelehlhehlleeheehelheelleeleelhllllllellelolelellelleoleloloelellololol olollollelllolllol lloo醇○◎升LO lolooloooloo厕所LOWOLO 0 1 OLW WOWO大号WLLOLOW L O,○○○○WOWW低öoOow WWW WOW wowooWWWO oOWRWOoorW¯¯行oOWorororWRRWLR rLROwoRWLWOworo WorrrRWl流öWRLR OLWöOWLDol rollWWLDWowDLlroWWořoWDWOL dorRrwrolrdrrorlrLWDRdodRLowdllrllolrdlrddolrdlrldowldorowlrdlrorloLDLWDLoddlrddlrdldldldrrdordldrlrddrodlrrldoldlrlddldlrdlldlrdlddrlddldddlddlddd

每次发送 h 键事件时,宏都会触发自身。

如何阻止Fn再次被调用,而另一个实例仍在运行?这是小型应用程序的主要功能,所以没有什么可以真正担心的关于与。的兼容性。

我天真的尝试修复 这是在mut running中添加main变量,callback在运行时设置为true,如果已经为immediately则返回extern crate inputbot; use std::time::Duration; use std::thread::sleep; fn main() { let mut running = false; let mut callback = || { if running { return }; running = true; inputbot::KeySequence("Hello World").send(); // wait to make sure keyboard events are done. sleep(Duration::from_millis(125)); running = false; }; inputbot::KeybdKey::HKey.bind(callback); inputbot::handle_input_events(); }

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`

但是,这不会编译:

  

Fn

经过一些阅读后,我的理解是,.bind()闭包(inputbot的mut方法所需)不能拥有任何可变数据,如捕获的mut变量。

也许可以将变量包装在某种非fn main() { let mut running = false; let lockedRunning = example::Lock(&running); let mut callback = || { { let mut running = lockedRunning.acquire(); if running { return }; running = true; } inputbot::KeySequence("Hello World").send(); // wait to make sure keyboard events are done. sleep(Duration::from_millis(125)); { let mut running = lockedRunning.acquire(); running = false; } }; } 值中?也许是某种锁,以使潜在的并发安全,就像这个伪咒?

{{1}}

2 个答案:

答案 0 :(得分:5)

这里你想要的是这个功能本身是互斥的。

Rust允许您使用Mutex结构执行此操作。它允许你持有一个锁定,当被获取时阻止其他人取下它直到你释放它。

具体来说,您需要的功能是try_lock方法,它允许您检查锁是否已被获取并允许您处理该情况。

let lock = mutex.try_lock();

match lock {
    Ok(_) => {
       // We are the sole owners here
    }
    Err(TryLockError::WouldBlock) => return,
    Err(TryLockError::Poisoned(_)) => {
        println!("The mutex is poisoned");
        return
    }
}

答案 1 :(得分:5)

使用原子值比Mutex稍微简单一点,因为您不需要担心失败案例,并且可以在不使用惰性静态的情况下轻松地将其变为静态变量:

use std::sync::atomic::{AtomicBool, Ordering};

fn main() {
    let is_being_called = AtomicBool::new(false);

    bind(move || {
        if !is_being_called.compare_and_swap(false, true, Ordering::SeqCst) {
            print!("I'm doing work");
            is_being_called.store(false, Ordering::SeqCst);
        }
    });
}

我有一种预感,这比使用Mutex更有效,因为不需要进行堆分配,但我没有对它进行基准测试。

如果您处于单线程上下文中并且您的回调以某种方式(意外地?)递归(which closures cannot be),您还可以使用Cell

use std::cell::Cell;

fn main() {
    let is_being_called = Cell::new(false);

    bind(move || {
        if !is_being_called.get() {
            is_being_called.set(true);
            print!("doing work");
            is_being_called.set(false);
        }
    })
}

如果碰巧有FnMut闭包,你甚至不需要Cell,只能使用布尔值:

fn main() {
    let mut is_being_called = false;

    bind(move || {
        if !is_being_called {
            is_being_called = true;
            print!("doing work");
            is_being_called = false;
        }
    })
}