问题描述
我正在尝试了解节流和去抖动是如何工作的,因为我现在在我的应用程序中需要它们。
I have found this article here that explains it pretty well
据我所知,
节流限制了在确定的时间内可以调用函数的次数。例如,只允许 xFunction 每 5 秒触发一次。
自上次调用以来经过给定的时间后,Debounce 会触发 xFunction。例如,如果用户单击鼠标 1000 次,xFunction 将在上次调用后 5 秒触发。
为了更好地理解节流以及上面链接文章中提供的类的方式,我创建了一个控制台应用程序,如果用户按下任意键,控制台应用程序将显示该键。
我现在正在尝试限制显示密钥的次数,但是我的代码似乎不起作用。每当我按下一个键时,就会显示我按下的键,但是 hello()
永远不会运行。
这是我所拥有的:
class Program
{
static void Main(string[] args)
{
Debouncedispatcher debouncedispatcher = new Debouncedispatcher();
ConsoleKeyInfo keyinfo;
keyinfo = Console.ReadKey();
do
{
keyinfo = Console.ReadKey();
debouncedispatcher.Throttle(5,param => hello());
}
while (keyinfo.Key != ConsoleKey.X);
}
private static void OnTimedEvent(object source,ElapsedEventArgs e)
{
hello();
}
private static void hello()
{
Console.WriteLine("Hello World 5 seconds");
}
}
这里是 DebouncingThrottling 类,您还可以找到 here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace SyncManager
{
public class Debouncedispatcher
{
private dispatcherTimer timer;
private DateTime timerStarted { get; set; } = DateTime.UtcNow.AddYears(-1);
/// <summary>
/// Debounce an event by resetting the event timeout every time the event is
/// fired. The behavior is that the Action passed is fired only after events
/// stop firing for the given timeout period.
///
/// Use Debounce when you want events to fire only after events stop firing
/// after the given interval timeout period.
///
/// Wrap the logic you would normally use in your event code into
/// the Action you pass to this method to debounce the event.
/// Example: https://gist.github.com/RickStrahl/0519b678f3294e27891f4d4f0608519a
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
/// <param name="priority">optional priorty for the dispatcher</param>
/// <param name="disp">optional dispatcher. If not passed or null Currentdispatcher is used.</param>
public void Debounce(int interval,Action<object> action,object param = null,dispatcherPriority priority = dispatcherPriority.ApplicationIdle,dispatcher disp = null)
{
// kill pending timer and pending ticks
timer?.Stop();
timer = null;
if (disp == null)
disp = dispatcher.Currentdispatcher;
// timer is recreated for each event and effectively
// resets the timeout. Action only fires after timeout has fully
// elapsed without other events firing in between
timer = new dispatcherTimer(TimeSpan.FromMilliseconds(interval),priority,(s,e) =>
{
if (timer == null)
return;
timer?.Stop();
timer = null;
action.Invoke(param);
},disp);
timer.Start();
}
/// <summary>
/// This method throttles events by allowing only 1 event to fire for the given
/// timeout period. Only the last event fired is handled - all others are ignored.
/// Throttle will fire events every timeout ms even if additional events are pending.
///
/// Use Throttle where you need to ensure that events fire at given intervals.
/// </summary>
/// <param name="interval">Timeout in Milliseconds</param>
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
/// <param name="param">optional parameter</param>
/// <param name="priority">optional priorty for the dispatcher</param>
/// <param name="disp">optional dispatcher. If not passed or null Currentdispatcher is used.</param>
public void Throttle(int interval,dispatcher disp = null)
{
// kill pending timer and pending ticks
timer?.Stop();
timer = null;
if (disp == null)
disp = dispatcher.Currentdispatcher;
var curTime = DateTime.UtcNow;
// if timeout is not up yet - adjust timeout to fire
// with potentially new Action parameters
if (curTime.Subtract(timerStarted).TotalMilliseconds < interval)
interval -= (int)curTime.Subtract(timerStarted).TotalMilliseconds;
timer = new dispatcherTimer(TimeSpan.FromMilliseconds(interval),disp);
timer.Start();
timerStarted = curTime;
}
}
}
感谢任何和所有帮助。
我检查过的其他链接试图帮助我理解: timers dispatcher youtube video
解决方法
Throttle
的实现有问题。
我改成这个
private CancellationTokenSource cts = new CancellationTokenSource();
private DateTime timerStarted { get; set; } = DateTime.UtcNow.AddYears(-1);
public void Throttle(int interval,Action<object> action,object param = null)
{
cts.Cancel();
cts = new CancellationTokenSource();
var curTime = DateTime.UtcNow;
if (curTime.Subtract(timerStarted).TotalMilliseconds < interval)
interval -= (int)curTime.Subtract(timerStarted).TotalMilliseconds;
Task.Run(async delegate
{
await Task.Delay(interval,cts.Token);
action.Invoke(param);
});
timerStarted = curTime;
}
并以 5000 毫秒的延迟调用它(正如我在对您的问题的评论中指出的那样,您只创建了 5 毫秒的延迟。
debounceDispatcher.Throttle(5000,_ => hello());
基本上我只是启动一个休眠的任务,然后调用该操作。如果取消标记被取消,它在 Task.Delay
中休眠时,任务将被取消,而不是运行操作。
您可能还需要更改 Debounce
以使用可取消的 Task
。基于 Task
的实现的优点是不需要从原始实现中引用的 WindowsBase 包。
就你的理解而言,你已经明白了。只是实施似乎存在缺陷。