多个计时器的多个数据库检查



我正在构建一个Winforms应用程序,该应用程序对包含敏感数据的数据库进行多次检查。

数据库以高频率全天候更新,因此应用程序将全天候检查数据库。我的最高要求是构建应用程序模块化。这意味着,如果我将来需要向应用程序添加额外的检查,我可以满怀信心地添加这些检查,以确保我不会弄乱现有的检查。此外,每个检查都需要能够自己启用/禁用。

我想通过建立额外的复选框&应用程序中每个检查的计时器组合。这样,任何额外的检查都是独立的(有自己的定时器和逻辑(,添加新的检查不会改变现有检查中的一行代码。

代码(测试应用程序(:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void check1CheckBox_CheckedChanged(object sender, EventArgs e)
{
check1Timer.Enabled = check1CheckBox.Checked;
}
private void check2CheckBox_CheckedChanged(object sender, EventArgs e)
{
check2Timer.Enabled = check2CheckBox.Checked;
}
private void check3CheckBox_CheckedChanged(object sender, EventArgs e)
{
check3Timer.Enabled = check3CheckBox.Checked;
}
private void check1Timer_Tick(object sender, EventArgs e)
{
check1(); //check DB data
}
private void check2Timer_Tick(object sender, EventArgs e)
{
check2(); //check DB data
}
private void check3Timer_Tick(object sender, EventArgs e)
{
check3(); //check DB data
}
}

我有两个问题:1.如果在上面解释的方法中,每个检查都是独立的,并且没有办法中断/干扰其他检查?2.在稳定性/响应性/定时方面,添加多个同时运行的定时器(10+(的"成本"是多少?

没有一个计时器会长时间阻塞表单,每个耗时的DB调用都将是异步的(通过等待调用(。

在实践中:大多数检查需要以2-5秒的频率运行,最大频率为每秒。每一次检查都有自己的频率和真实性。

谢谢。

我能看到的唯一可能的问题是事件处理程序的重叠调用问题,这可能是一个相当严重的问题。从.NET平台提供的三个计时器(System.Windows.Forms.TimerSystem.Timers.TimerSystem.Threading.Timer(中,没有一个可以防止重叠,这意味着在完成前一个查询之前,可以再次向数据库发送相同的查询。结果是,数据库最终可能会被超出其处理能力的请求轰炸。考虑到数据库有一天比一天大的趋势,导致查询速度越来越慢,具有恒定间隔的多个计时器可能会成为应用程序、数据库或整个系统健康的定时炸弹。

以下是一些与计时器的重叠行为有关的问题,答案为该问题提供了解决方案:

  1. 同步计时器以防止重叠
  2. 如果上一个线程仍然繁忙,如何让Timer skip勾选
  3. 执行期间定时事件重叠

这是我自己的非重叠Timer的实现,如果我有类似的问题需要解决,我更喜欢这种行为。

/// <summary>
/// Provides a mechanism for executing a method on a thread pool thread,
/// at intervals that are automatically extended to prevent overlapping.
/// </summary>
public class NonOverlappingTimer
{
private readonly object _locker = new object();
private readonly Func<Task> _function;
private CancellationTokenSource _cts = new CancellationTokenSource();
private bool _enabled = false;
private int _interval = 100;
public NonOverlappingTimer(Action action)
{
if (action == null) throw new ArgumentNullException(nameof(action));
_function = () => Task.Run(action);
AsyncLoop();
}
public NonOverlappingTimer(Func<Task> function)
{
if (function == null) throw new ArgumentNullException(nameof(function));
_function = () => Task.Run(function);
AsyncLoop();
}
private async void AsyncLoop()
{
while (true)
{
CancellationToken ct;
bool enabled;
int interval;
lock (_locker)
{
ct = _cts.Token;
enabled = _enabled;
interval = _interval;
}
var delayTask = Task.Delay(enabled ? interval : Timeout.Infinite, ct);
if (enabled) await _function().ConfigureAwait(false);
try
{
await delayTask.ConfigureAwait(false);
}
catch (OperationCanceledException) { } // Suppress this exception
}
}
public bool Enabled
{
get
{
lock (_locker) return _enabled;
}
set
{
CancellationTokenSource cts;
lock (_locker)
{
if (value == _enabled) return;
_enabled = value;
cts = _cts;
_cts = new CancellationTokenSource();
}
cts.Cancel();
}
}
public int Interval
{
get
{
lock (_locker) return _interval;
}
set
{
if (value < 0) throw new ArgumentOutOfRangeException(nameof(value));
CancellationTokenSource cts;
lock (_locker)
{
if (value == _interval) return;
_interval = value;
cts = _cts;
_cts = new CancellationTokenSource();
}
cts.Cancel();
}
}
}

用法示例:

var timer = new NonOverlappingTimer(async () =>
{
Console.WriteLine("Tick");
await Task.Delay(3000); // Simulate a lengthy I/O-bound operation
});
timer.Interval = 2000;
timer.Enabled = true;

输出:每3秒将打印一次"刻度",尽管间隔为2秒。

最新更新