说到 Throttle,在网页前端中运用得较多,比如页面滚动、即时搜索的输入事件,这类事件触发非常频繁,如果每次都进行业务操作,消耗会非常大。这时采用 Throttle 进行限流,当函数执行后一段时间内不重复执行,俗称冷却。在 C++ 开发中,有时也会遇到相似的场景:在一个不停执行的循环中需要定时进行某些操作,常规的处理方式是类似这样:
1 2 3 4 5 6 7 8 9 10 11
| time_t begin = time(0); while(!stop) { if(time(0) - begin > 5) { begin = time(0); doSomething(); }
doOtherBiz(); }
|
看起来简单明了的解决了问题,现在如果再来一个需要定时执行的操作,那么代码需要改成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| time_t begin1 = time(0); time_t begin2 = time(0); while(!stop) { if(time(0) - begin1 > 5) { begin1 = time(0); doSomething1(); }
if(time(0) - begin2 > 10) { begin2 = time(0); doSomething2(); }
doOtherBiz(); }
|
看起来不太妙,让我们把这种处理方式简单封装一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <chrono>
using std::chrono::duration_cast; using std::chrono::milliseconds; using std::chrono::system_clock;
class Throttle { int64_t m_lastTm = 0; int64_t m_interval = 0; std::function<void()> m_func = nullptr;
int64_t now() { return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); } public: Throttle(int64_t interval, std::function<void()> func) { m_interval = interval; m_func = func; }
void operator () (void) { if (m_lastTm + m_interval <= now()) { m_lastTm = now(); m_func(); } } };
|
可以这么使用,是不是方便了很多呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Throttle doSomething1(5000, [&]() { ... });
Throttle doSomething2(10000, [&]() { ... });
while(!stop) { doSomething1(); doSomething2(); doOtherBiz(); }
|
与 Javascript
用定时器实现的 Throttle 不同的是,跳过执行的调用在冷却过去后并不会自动执行,不过在高频运行的循环中这点问题可以忽略不计了。