Skip to content

Instantly share code, notes, and snippets.

@bdrum
Last active August 12, 2022 11:15
Show Gist options
  • Save bdrum/5c80e362ace2cef325325235ab7efc57 to your computer and use it in GitHub Desktop.
Save bdrum/5c80e362ace2cef325325235ab7efc57 to your computer and use it in GitHub Desktop.
Primitive watcher for property changes without events

Реализация примитивного наблюдателя за свойством

По-хорошему это надо делать через async/await, однако, если интерфейс жестко привязан к значению счетчика, то есть пока он не изменился никакой работы не предусматривается, то можно опустить это

Синхронно, однопоточно.

Представим, что есть такой класс контроллера в библиотеке:

class Controller
{
  public static uint Counter 
  {
    get
    {
      Thread.Sleep(TimeSpan.FromSeconds(1)); // Пауза имитирующая соединение с прибором итд
      return (uint)(3 * (new Random()).NextDouble() + 1);
     }
  }
}

У него нет события, которое вызывалось бы при изменении счетчика, а нам нужно отслеживать его изменение.

Очевидно, что так как мы работаем синхронно и в одном потоке, нам важно чтобы программа не повисла. Поэтому нам нужно два параметра - время задержки перед запросом значения счетчика, чтобы прибор не грузился. И число обращений к значению счетчика.

Эти два параметра лучше вынести в сам интерфейс, чтобы можно было их менять во время работы программы. Это может пригодиться.

class MyForm
{
  public void CounterWatcher()
  {
    Console.WriteLine("Start observing for Contoroller");
    var init = Controller.Counter;
    var numberOfAttempts = 5;
    var pauseTimeSec = 1;
    var n = 0;
    Console.WriteLine($"Initial value is {init}");

    while (Controller.Counter != init  && n < numberOfAttempts)
    {
      Thread.Sleep(TimeSpan.FromSeconds(pauseTimeSec));
      Console.WriteLine($"Value was not changed. Try again. Number of attempt is {n}");
      n++;
    }

    if (numberOfAttempts == 4)
    {
      Console.WriteLine("Controller.Counter was not changed");
      return;
    }
    Console.WriteLine("Controller.Counter has changed");

  }
}

Вывод такой программы:

Start observing for Contoroller
Initial value is 2
Value was not changed. Try again. Number of attempt is 0
Controller.Counter has changed

С использованием TPL

Другой вариант - использовать класс Task, который запускает код в потоке параллельно с главным, но так как мы будем ждать его завершения, это все равно будет подвешивтаь главный поток.

Кроме того, внутри потока отличного от главного в WinForms нельзя обращаться к элементам форм, например, кнопкам, лейблам и так далее.

Однако это может быть удобно тем, что предоставляет встроенный и гибкий механизм отмены:

class MyForm
{
  public static void CounterWatcher()
  {
    Console.WriteLine("Start observing for Contoroller");
    var init = Controller.Counter;
    var numberOfAttempts = 5;
    var pauseTimeSec = 1;
    Console.WriteLine($"Initial value is {init}");

    var canc = new CancellationTokenSource();
    canc.CancelAfter(TimeSpan.FromSeconds(pauseTimeSec*numberOfAttempts));
    try
    {
      var watchTask = Task.Run(() =>
      {
        while (Controller.Counter != init)
        {
          Thread.Sleep(TimeSpan.FromSeconds(pauseTimeSec));
          if (canc.Token.IsCancellationRequested) throw new OperationCanceledException();
          Console.WriteLine($"Value has not changed.");
        }
      }, canc.Token);

      watchTask.Wait();
      Console.WriteLine("Controller.Counter has changed");
    }
    catch (AggregateException ae)
    {
      foreach (var ie in ae.InnerExceptions)
      {
        if (ie is OperationCanceledException)
          Console.WriteLine("Controller.Counter was not changed");
        else
          throw; // any other exception will throw again
      }
    }
}

Вывод:

# В случае времени на выполнение 1 сек:
Start observing for Contoroller
Initial value is 3
Controller.Counter was not changed

# В случае времени на выполнение 5 сек:
Start observing for Contoroller
Initial value is 1
Value has not changed.
Controller.Counter has changed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment