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

Han*_*les 2 macros concurrency keyboard-shortcuts rust

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

你好,世界

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

extern crate inputbot;

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

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

    inputbot::handle_input_events();
}
Run Code Online (Sandbox Code Playgroud)

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

hHHHHHHHHHHHHHHHHHHHHHHHHHEHEHhEhEEHHhEhEhEHhEHHEHHEEHhEHlhEHEHHEHLEHLHeeleleelelelllelelleelehlhehlleeheehelheelleeleelhllllllellelolelellelleoleloloelellololol olollollelllolllol lloo醇○◎升LO lolooloooloo厕所LOWOLO OL OLW WOWO大号WLLOLOW LOOOOö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();
}
Run Code Online (Sandbox Code Playgroud)

但是,这不编译:

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

经过一些阅读后,我的理解是,Fn闭包(inputbot .bind()方法所需)不能拥有任何可变数据,如捕获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;
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

Nei*_*kos 5

你想要的是这个功能与它自己是互斥的.

Rust允许您使用Mutexstruct 执行此操作.它允许你持有一个锁定,当获得阻止任何其他人取得它,直到你释放它.

具体来说,您需要的功能是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
    }
}
Run Code Online (Sandbox Code Playgroud)


She*_*ter 5

使用原子值比a更简单,Mutex因为您不需要担心失败案例,并且可以在不使用lazy-static的情况下轻松地将其变为静态变量:

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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

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

如果您处于单线程上下文中并且您的回调以某种方式(意外地?)递归(哪些闭包不可能),您还可以使用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);
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

如果碰巧有一个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;
        }
    })
}
Run Code Online (Sandbox Code Playgroud)