C++ 函数消抖
函数消抖 指在短时间内连续多次调用同一函数,仅最后一次调用生效。
形如:
1auto debouncedFn = debounce(fn, 100);通常将需要消抖的函数封装成一个新的函数,新的函数进行延迟后调用原函数:
- 如果在延时结束前再次调用,则重新开始延时
- 如果在延时结束前没有再次调用,则使调用原函数
示例
这个实现存在一个问题,那就是通过
thread.join();等待上一次调用取消,会产生阻塞,不能用于实际开发。
1#include <functional>
2#include <chrono>
3#include <thread>
4#include <mutex>
5#include <future>
6#include <memory>
7#include <exception>
8
9namespace Utils
10{
11
12template<typename R, typename... ARGS>
13std::enable_if_t<!std::is_void_v<R> && (!std::is_void_v<ARGS> && ...), void>
14invoke(std::shared_ptr<std::promise<R>> promise, std::function<R(ARGS...)> fn, ARGS... args)
15{
16 promise->set_value(fn(args...));
17}
18
19template<typename R>
20void invoke(std::shared_ptr<std::promise<R>> promise, std::function<R(void)> fn)
21{
22 promise->set_value(fn());
23}
24
25template<typename... ARGS>
26void invoke(std::shared_ptr<std::promise<void>> promise, std::function<void(ARGS...)> fn, ARGS... args)
27{
28 fn(args...);
29 promise->set_value();
30}
31
32void invoke(std::shared_ptr<std::promise<void>> promise, std::function<void(void)> fn)
33{
34 fn();
35 promise->set_value();
36}
37
38/*************************************************************************
39 * @brief 函数消抖
40 * @param fn 希望消抖函数
41 * @param ms 消抖的延时时间,毫秒
42 * @return 一个封装了消抖功能的函数
43 *************************************************************************/
44template<typename R, typename... ARGS>
45std::function<std::shared_ptr<std::promise<R>>(ARGS...)> debounce(std::function<R(ARGS...)> fn, int ms)
46{
47 auto lambda = [fn = std::move(fn), ms](ARGS... args) mutable -> std::shared_ptr<std::promise<R>> {
48 std::shared_ptr<std::promise<R>> promise = std::make_shared<std::promise<R>>();
49 static auto lastCallTime = std::chrono::steady_clock::now();
50 static bool cancel = false;
51 static std::thread thread;
52 static std::mutex mutex;
53
54 if (thread.joinable())
55 {
56 mutex.lock();
57 cancel = true;
58 mutex.unlock();
59
60 thread.join();
61
62 mutex.lock();
63 cancel = false;
64 mutex.unlock();
65 }
66
67 thread = std::thread([fn, ms, promise, args...]() mutable {
68 std::this_thread::sleep_until(lastCallTime + std::chrono::milliseconds(ms));
69
70 mutex.lock();
71 if (cancel)
72 {
73 mutex.unlock();
74 promise->set_exception(std::make_exception_ptr(std::runtime_error("cancel")));
75 return;
76 }
77 mutex.unlock();
78
79 // promise->set_value(fn(args...));
80 invoke(promise, fn, args...);
81 thread.detach();
82 });
83
84 return promise;
85 };
86
87 return lambda;
88}
89
90template<typename R, typename... ARGS>
91std::function<std::shared_ptr<std::promise<R>>(ARGS...)> debounce(R(*fn)(ARGS...), int ms)
92{
93 return debounce(static_cast<std::function<R(ARGS...)>>(fn), ms);
94}
95
96}; // namespace Utils测试
1int main()
2{
3 auto fn = Utils::debounce(test, 1000);
4 auto p1 = fn(1, 2);
5 auto p2 = fn(3, 4);
6
7 auto f1 = p1->get_future();
8 auto f2 = p2->get_future();
9
10 f1.wait();
11 f2.wait();
12
13 try {
14 if (f1.valid())
15 printf("%d\n", f1.get());
16 } catch (std::runtime_error e) {
17 printf("%s\n", e.what());
18 }
19
20 try {
21 if (f2.valid())
22 printf("%d\n", f2.get());
23 } catch (std::runtime_error e) {
24 printf("%s\n", e.what());
25 }
26
27 return 0;
28}结果:
1test 3 4
2cancel
37