#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();
}
}
Но у меня в голове есть два сомнения,
-
Если есть 10 наблюдателей и интервал моего таймера равен 60 секундам, то оператор new Thread() будет запущен 600 раз.
Эффективно ли и рекомендуется ли создавать новые потоки при каждом тике таймера?
-
Что, если моим наблюдателям требуется слишком много времени для завершения их логики обновления, т. Е. проходит более 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’ не должен вызываться снова, если он не завершил свою последнюю работу.