Несколько проверок БД несколькими таймерами - PullRequest
1 голос
/ 20 апреля 2020

Я создаю приложение Winforms, которое выполняет несколько проверок для БД, содержащей конфиденциальные данные.

БД обновляется с высокой частотой 24/7, поэтому приложение будет проверять БД 24/7. Наивысшее требование, которое у меня есть, - это модульное приложение. Это означает, что если мне нужно добавить дополнительные проверки в приложение в будущем, я могу добавить это с большой уверенностью, что я не испорчу существующие проверки. Кроме того, каждая проверка должна иметь возможность включать / отключать ее самостоятельно.

Я подумал сделать это, создав дополнительную комбинацию Check Box & Timer для каждой проверки в приложении. Таким образом, любая дополнительная проверка является независимой (имеет собственный таймер и лог c), и добавление новой проверки не изменит ни одной строки кода в существующих проверках.

Код (тестовое приложение):

   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
    }
}

У меня есть 2 вопроса: 1. Если в методе, описанном выше, каждая проверка независима и не имеет возможности прервать / связать с другими проверками? 2. Какова «стоимость» добавления множества таймеров (10+), запущенных в одно и то же время, либо в режиме стабильности / отзывчивости / времени?

Ни один из таймеров не будет блокировать форму в течение длительного времени, каждый длительный вызов БД будет Asyn c (при ожидании вызовов).

На практике: большинство проверок должны выполняться с частотой 2-5 секунд, максимальная частота будет каждую секунду. Каждый чек имеет свою частоту и проверяем.

Спасибо.

1 Ответ

0 голосов
/ 21 апреля 2020

Единственная возможная проблема, которую я вижу, которая может быть довольно серьезной, - это проблема перекрывающихся вызовов обработчиков событий. Из трех таймеров, предоставленных платформой. NET (System.Windows.Forms.Timer, System.Timers.Timer и System.Threading.Timer), ни один из них не предотвращает перекрытие, Это означает, что тот же запрос может быть снова отправлен в базу данных до завершения предыдущего запроса. В результате база данных может быть засыпана большим количеством запросов, чем она может обработать. Учитывая, что базы данных имеют тенденцию увеличиваться с каждым днем, в результате чего запросы, которые становятся все медленнее, наличие нескольких таймеров с постоянными интервалами, может стать бомбой замедленного действия для работоспособности приложения, базы данных или системы в целом .

Вот несколько вопросов, связанных с перекрывающимся поведением таймеров, с ответами, предлагающими решения проблемы:

  1. Синхронизация таймера для предотвращения наложения
  2. Как позволить таймеру пропустить тик, если предыдущий поток все еще занят
  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 секунды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...