c#节流阀示例
c# Throttle Example
我正在尝试了解节流和去抖动是如何工作的,因为我现在在我的应用程序中需要它们。
I have found this article here that explains it pretty well
据我了解,
节流限制在确定的时间内可以调用函数的次数。例如,只允许 xFunction 每 5 秒触发一次。
自上次调用 xFunction 后经过一定时间后,去抖动将触发 xFunction。例如,如果用户单击鼠标 1000 次,xFunction 将在上次调用后 5 秒触发。
为了更好地理解节流以及上面链接的文章中 class 如何提供,我创建了一个控制台应用程序,如果用户在其中按下任意键,控制台应用程序将显示该键。
我现在正试图限制显示该键的次数,但我的代码似乎不起作用。每当我按下一个键时,都会显示我按下的键,但是 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 class 你也可以找到 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, 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;
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), priority, (s, e) =>
{
if (timer == null)
return;
timer?.Stop();
timer = null;
action.Invoke(param);
}, 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 包。
就你的理解而言,你已经明白了。只是执行似乎有缺陷。
我正在尝试了解节流和去抖动是如何工作的,因为我现在在我的应用程序中需要它们。
I have found this article here that explains it pretty well
据我了解,
节流限制在确定的时间内可以调用函数的次数。例如,只允许 xFunction 每 5 秒触发一次。
自上次调用 xFunction 后经过一定时间后,去抖动将触发 xFunction。例如,如果用户单击鼠标 1000 次,xFunction 将在上次调用后 5 秒触发。
为了更好地理解节流以及上面链接的文章中 class 如何提供,我创建了一个控制台应用程序,如果用户在其中按下任意键,控制台应用程序将显示该键。
我现在正试图限制显示该键的次数,但我的代码似乎不起作用。每当我按下一个键时,都会显示我按下的键,但是 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 class 你也可以找到 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, 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;
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), priority, (s, e) =>
{
if (timer == null)
return;
timer?.Stop();
timer = null;
action.Invoke(param);
}, 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 包。
就你的理解而言,你已经明白了。只是执行似乎有缺陷。