Шаблон наблюдателя с таймером

#c# #.net #multithreading #oop #design-patterns

#c# #.net #многопоточность #ооп #шаблоны проектирования

Вопрос:

Я использовал шаблон Observer для своего приложения.

У меня есть тема, у которой есть одна система.Таймеры. Объект Timer в нем с именем ‘tmr’. Событие tick этого таймера срабатывает через каждые 60 секунд. Об этом событии tick я уведомлю всех моих наблюдателей, которые прикреплены к моей теме. Я использовал цикл for для перебора моего списка наблюдателей, а затем запустил метод Observers Update.

Предположим, у меня есть 10 наблюдателей, прикрепленных к моему объекту.

Для завершения обработки каждому наблюдателю требуется 10 секунд.

Теперь уведомление, выполняемое в цикле for, вызывает вызов метода обновления последнего наблюдателя через 90 секунд. т. е. Следующий метод обновления наблюдателя вызывается только после завершения обработки предыдущего.

Но это не то, что я хотел в своем приложении. Мне нужно, чтобы все мои методы обновления наблюдателей запускались мгновенно при срабатывании таймера. Чтобы ни одному наблюдателю не приходилось ждать. Я надеюсь, что это можно сделать с помощью потоков.

Итак, я изменил код, чтобы,

 // Fires the updates instantly
    public void Notify()
    {
      foreach (Observer o in _observers)
      {
        Threading.Thread oThread = new Threading.Thread(o.Update);
        oThread.Name = o.GetType().Name;
        oThread.Start();
      }
    }
  

Но у меня в голове есть два сомнения,

  1. Если есть 10 наблюдателей и интервал моего таймера равен 60 секундам, то оператор new Thread() будет запущен 600 раз.

    Эффективно ли и рекомендуется ли создавать новые потоки при каждом тике таймера?

  2. Что, если моим наблюдателям требуется слишком много времени для завершения их логики обновления, т. Е. проходит более 60 секунд. Означает, что отсчет таймера происходит до обновления наблюдателей. Как я могу это контролировать?

Я могу опубликовать пример кода .. если требуется…

Код, который я использовал..

 using System;
using System.Collections.Generic;
using System.Timers;
using System.Text;
using Threading = System.Threading;
using System.ComponentModel;

namespace singletimers
{
  class Program
  {


    static void Main(string[] args)
    {
      DataPullerSubject.Instance.Attach(Observer1.Instance);
      DataPullerSubject.Instance.Attach(Observer2.Instance);
      Console.ReadKey();
    }
  }

  public sealed class DataPullerSubject
  {
    private static volatile DataPullerSubject instance;
    private static object syncRoot = new Object();
    public static DataPullerSubject Instance
    {
      get
      {
        if (instance == null)
        {
          lock (syncRoot)
          {
            if (instance == null)
              instance = new DataPullerSubject();
          }
        }

        return instance;
      }
    }

    int interval = 10 * 1000;
    Timer tmr;
    private List<Observer> _observers = new List<Observer>();

    DataPullerSubject()
    {
      tmr = new Timer();
      tmr.Interval = 1; // first time to call instantly
      tmr.Elapsed  = new ElapsedEventHandler(tmr_Elapsed);
      tmr.Start();
    }

    public void Attach(Observer observer)
    {
      _observers.Add(observer);
    }

    public void Detach(Observer observer)
    {
      _observers.Remove(observer);
    }

    // Fires the updates instantly
    public void Notify()
    {
      foreach (Observer o in _observers)
      {
        Threading.Thread oThread = new Threading.Thread(o.Update);
        oThread.Name = o.GetType().Name;
        oThread.Start();
      }
    }

    private void tmr_Elapsed(object source, ElapsedEventArgs e)
    {
      tmr.Interval = interval;
      tmr.Stop(); // stop the timer until all notification triggered
      this.Notify();
      tmr.Start();//start again
    }
  }


  public abstract class Observer
  {
    string data;
    public abstract void Update();
    public virtual void GetDataFromDBAndSetToDataSet(string param)
    {
      Console.WriteLine("Processing for: "   param);
      data = param   new Random().Next(1, 2000);
      Threading.Thread.Sleep(10 * 1000);//long work
      Console.WriteLine("Data set for: "   param);
    }
  }


  public sealed class Observer1 : Observer
  {
    private static volatile Observer1 instance;
    private static object syncRoot = new Object();
    public static Observer1 Instance
    {
      get
      {
        if (instance == null)
        {
          lock (syncRoot)
          {
            if (instance == null)
              instance = new Observer1();
          }
        }

        return instance;
      }
    }
    Observer1()
    {
    }
    public override void Update()
    {
      base.GetDataFromDBAndSetToDataSet("Observer1");
    }

  }

  public sealed class Observer2 : Observer
  {
    private static volatile Observer2 instance;
    private static object syncRoot = new Object();
    public static Observer2 Instance
    {
      get
      {
        if (instance == null)
        {
          lock (syncRoot)
          {
            if (instance == null)
              instance = new Observer2();
          }
        }

        return instance;
      }
    }
    Observer2()
    {
    }
    public override void Update()
    {
      base.GetDataFromDBAndSetToDataSet("Observer2");
    }

  }
}
  

Спасибо и с наилучшими пожеланиями.

Комментарии:

1. существуют лучшие способы реализации шаблона singleton в c # — см. yoda.arachsys.com/csharp/singleton.html

Ответ №1:

  • Использование new Thread не рекомендуется. Используйте Task или Task<T>
  • Ваша лучшая попытка создать структуру Observable pattern, вероятно, приблизится только к Rx. Используйте тот, который решает проблемы, о которых вы упомянули (т. Е. Если обработка занимает слишком много времени). Rx предоставит вам огромную гибкость в определении ваших наблюдаемых сценариев.

Ответ №2:

1) Вы можете использовать потоки из пула потоков через ThreadPool.QueueUserWorkItem или вы можете использовать задачи

2) Вы должны синхронизировать свои методы.

Ответ №3:

В качестве альтернативы наблюдатели могли бы реализовать обновление неблокирующим способом. То есть обновление всегда возвращается немедленно. Тогда объекты-наблюдатели несут ответственность за выполнение своей работы в новом потоке, если это необходимо.

Я не уверен, помогает ли это в вашем сценарии — я не знаю, что такое ваши «Наблюдатели», но тогда, может быть, вы тоже не знаете?

Комментарии:

1. @Garen: я не понял, что вы подразумеваете под «неблокирующим способом». В моих наблюдателях я получаю данные из базы данных. Так что здесь возможно, что это займет больше пары минут. При этом каждый наблюдатель обновляет свое общедоступное поле.

2. @thinkmmk — Я имею в виду, что вызов Update возвращается немедленно, и Наблюдатели несут ответственность за запуск операции обновления в отдельном потоке. наблюдатели также отвечают либо за постановку в очередь, либо за игнорирование вызовов обновления, когда обновление уже выполняется. Но от ваших наблюдателей зависит, будет ли это хорошим решением — вы хотите избежать дублирования одной и той же логики в разных классах ‘Observer’.

3. Я отредактировал свой исходный пост с помощью кода .. теперь я борюсь за синхронизацию, т. е. obesever ‘x’ не должен вызываться снова, если он не завершил свою последнюю работу.