更新:这适用于Windows 10。
这是一个简单的例子:
void testcase()
{
if (myTimer != null)
myTimer.Cancel();
myTimer = ThreadPoolTimer.CreateTimer(
t => myMethod(),
TimeSpan.FromMilliseconds(4000)
);
}
void myMethod()
{
myTimer = null;
//some work
}
它应该做的是确保mysethod不能在4s中比一次更频繁地调用,并且如果已经有更新的testcase调用,则不应该调用myMethod。与桌面上的.net计时器类似的东西是可能的。但是,对testcase的新调用并不会阻止以前安排的myMethods运行。我有一个简单的解决方法,通过向myMethod添加整数callid参数并跟踪它。但是上面的内容应该有效,而且没有。
我做错了吗?有没有人对如何做到更好?
答案 0 :(得分:5)
您正在寻找的是debouncing,at least in javascript。
实现它的一种简单方法是改用System.Threading.Timer
,它有一个方便的Change
用来重置它。
如果你想将它抽象成你自己的计时器类,它看起来像:
public class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
public DebounceTimer(Action callback, int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => callback(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
public void Reset()
{
// each call to Reset() resets the timer
_timer.Change(
dueTime: _delayInMs,
period: System.Threading.Timeout.Infinite);
}
public void Dispose()
{
// timers should be disposed when you're done using them
_timer.Dispose();
}
}
您的测试用例将成为:
private DebounceTimer _timer;
void Init()
{
// myMethod will be called 4000ms after the
// last call to _timer.Reset()
_timer = new DebounceTimer(myMethod, 4000);
}
void testcase()
{
_timer.Reset();
}
void myMethod()
{
//some work
}
public void Dispose()
{
// don't forget to cleanup when you're finished testing
_timer.Dispose();
}
<强> [更新] 强>
从您的评论中,您似乎想要在每次重置时更改回调方法,并且只调用最后一个。如果是这种情况,您可以将代码更改为:
class DebounceTimer : IDisposable
{
private readonly System.Threading.Timer _timer;
private readonly int _delayInMs;
private Action _lastCallback = () => { };
public DebounceTimer(int delayInMs)
{
_delayInMs = delayInMs;
// the timer is initially stopped
_timer = new System.Threading.Timer(
callback: _ => _lastCallback(),
state: null,
dueTime: System.Threading.Timeout.Infinite,
period: System.Threading.Timeout.Infinite);
}
public void Reset(Action callback)
{
_timer.Change(dueTime: _delayInMs, period: System.Threading.Timeout.Infinite);
// note: no thread synchronization is taken into account here,
// a race condition might occur where the same callback would
// be executed twice
_lastCallback = callback;
}
public void Dispose()
{
_timer.Dispose();
}
}
调用Reset
方法时,可以使用lambda捕获各种方法调用(不仅仅是Action
方法):
void testcase()
{
_timer.Reset(() => myMethod());
}
void othertestcase()
{
// it's still a parameterless action, but it
// calls another method with two parameters
_timer.Reset(() => someOtherMethod(x, y));
}
如第二个计时器片段的注释中所述,代码不是线程安全的,因为当在{内部更改回调引用时,计时器处理程序可能已在单独的线程上运行(或即将运行) {1}}方法,意味着同一个回调将被执行两次。
稍微复杂的解决方案是在更改回调时锁定,并进行额外检查,以确定自上次调用重置后是否已经过了足够的时间。最终的代码看起来像这样(可能还有其他的同步方式,但这个方法非常简单):
Reset
答案 1 :(得分:0)
问题是你在myMethod中设置了timer = null。这保证了在下次调用testCase时它将为null(因此它不会被取消)。
而是使用TimerPool.CreateTimer创建单实例计时器。它只会发射一次。当您的工作进程完成时,它应该做的最后一件事是初始化一个新的计时器。
答案 2 :(得分:0)
为了回答我自己可能出现的问题,似乎Cancel()仅用于取消定期计时器以进一步重复。我不能说文档准确地说明了这一点,但它似乎就是这样的。因此,如果计时器不像这种情况那样是周期性的,则取消无效。
更新:这在Windows 10中可以正常使用。