多个计时器的多个数据库检查
Posted
技术标签:
【中文标题】多个计时器的多个数据库检查【英文标题】:Multiple DB Checks by multiple Timers 【发布时间】:2020-08-03 10:35:37 【问题描述】:我正在构建一个 Winforms 应用程序,该应用程序对包含敏感数据的数据库进行多项检查。
数据库正在以 24/7 的高频率更新,因此应用程序将 24/7 检查数据库。 我的最高要求是构建应用程序模块化。 这意味着如果我将来需要向应用程序添加额外的检查,我可以非常自信地添加它,我不会弄乱现有的检查。 此外,每个检查都需要能够自行启用/禁用。
我想通过为应用程序中的每个检查构建额外的复选框和计时器组合来做到这一点。 这样,任何额外的检查都是独立的(有自己的计时器和逻辑),添加新的检查不会改变现有检查中的任何一行代码。
代码(测试应用程序):
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 调用都是异步的(通过 await 调用)。
在实践中:大多数检查需要以 2-5 秒的频率运行,最大频率将是每秒。 每张支票都有自己的频率和可验证性。
谢谢。
【问题讨论】:
【参考方案1】:我能看到的唯一可能的问题是事件处理程序的重叠调用问题,这可能是一个非常严重的问题。从 .NET 平台提供的三个计时器(System.Windows.Forms.Timer
、System.Timers.Timer
和System.Threading.Timer
)来看,它们都不能防止重叠,这意味着相同的查询可以在前一个查询完成之前再次发送到数据库。结果是数据库最终可能会受到比它处理能力更多的请求的轰炸。考虑到数据库有逐日变大的趋势,导致查询变得越来越慢,拥有多个具有恒定间隔的计时器可能会成为应用程序、数据库或整个系统健康的定时炸弹.
以下是一些与计时器重叠行为相关的问题,并提供了解决问题的答案:
-
Synchronizing a timer to prevent overlap
How to let Timer skip tick if the previous thread is still busy
Timed events overlapping during execution
这是我自己对非重叠 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 秒打印一次“Tick”,尽管间隔为 2 秒。
【讨论】:
以上是关于多个计时器的多个数据库检查的主要内容,如果未能解决你的问题,请参考以下文章
C# Windows 服务 - 我的计时器不工作,获得多个线程