Threading и Singletons - PullRequest
       10

Threading и Singletons

1 голос
/ 10 июня 2009

Прежде чем кто-нибудь расскажет мне о синглетах, я скажу, что в этом случае для меня имеет смысл иметь синглтон, учитывая широкое использование этого объекта во всем моем коде, и если у кого-то есть лучший способ, чем DI, я бы Хотелось бы услышать, но я надеюсь, что это не будет темой этого поста, более того, помочь решить его будет.

Как говорится, вот в чем проблема: Кажется, что через некоторое время я теряю ссылку на свой планировщик классов, и внутри появляется таймер, который больше не срабатывает. Это потому, что он используется в одноэлементном режиме, и когда он теряет ссылку, это GC'd?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace MessageQueueInterface
{
    public class Scheduler
    {
        private const int _interval = 1000;
        private readonly Dictionary<DateTime, Action> _scheduledTasks = new Dictionary<DateTime, Action>();
        private readonly Timer _mainTimer;

        public Scheduler()
        {
            _mainTimer = new Timer();
            _mainTimer.Interval = _interval;
            _mainTimer.Tick += MainTimer_Tick;
            _mainTimer.Start();
        }

        void MainTimer_Tick(object sender, EventArgs e)
        {
            CheckStatus();
        }

        public void Add(DateTime timeToFire, Action action)
        {
            lock (_scheduledTasks)
            {
                if (!_scheduledTasks.Keys.Contains(timeToFire))
                {
                    _scheduledTasks.Add(timeToFire, action);
                }
            }
        }

        public void CheckStatus()
        {
            Dictionary<DateTime, Action> scheduledTasksToRemove = new Dictionary<DateTime, Action>();
            lock (_scheduledTasks)
            {
                foreach (KeyValuePair<DateTime, Action> scheduledTask in _scheduledTasks)
                {
                    if (DateTime.Now >= scheduledTask.Key)
                    {
                        scheduledTask.Value.Invoke();
                        scheduledTasksToRemove.Add(scheduledTask.Key, scheduledTask.Value);
                    }
                }
            }
            foreach (KeyValuePair<DateTime, Action> pair in scheduledTasksToRemove)
            {
                _scheduledTasks.Remove(pair.Key);
            }
        }
    }
}

доступ к нему осуществляется в других классах следующим образом

ApplicationContext.Current.Scheduler.Add(DateTime.Now.AddSeconds(1), ResetBackColor);

ApplicationContext мой синглтон

также я знаю, что объект datetime - не лучший КЛЮЧ для словаря, но он подходит моим целям здесь

вот синглтон

public class ApplicationContext
{
    private static ApplicationContext _context;
    private Scheduler _scheduler;

    public Scheduler Scheduler
    {
        get { return _scheduler; }
    }

    private void SetProperties()
    {
        _scheduler = new Scheduler();
    }

    public static ApplicationContext Current
    {
        get
        {
            if (_context == null)
            {
                _context = new ApplicationContext();
                _context.SetProperties();

            }
            return _context;
        }
    }
}

Ответы [ 5 ]

3 голосов
/ 10 июня 2009

Класс, который вы опубликовали, не является вашим классом-одиночкой. Этот код был бы более полезным.

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

Размещение вашего одиночного кода позволило бы мне или кому-либо еще дать более конкретный ответ.

Редактировать

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

Я бы порекомендовал создать простой экземпляр object для синхронизации в качестве статического члена, а затем заблокировать его в получателе. Это должно предотвратить возникновение этого условия.

Кроме того, какова цель метода SetProperties() вместо инициализации переменной в конструкторе без параметров или даже в точке объявления? Наличие такой функции позволяет создать новый объект Scheduler и отказаться от существующего.

2 голосов
/ 10 июня 2009

Вы говорите, что проблема в том, что ваш глобальный / статический указатель на Scheduler () становится нулевым? Если это так, нам нужно увидеть код, который манипулирует этой ссылкой, чтобы понять, когда она терпит неудачу.

У меня есть некоторые отзывы, хотя ... Ваши вызовы _scheduledTasks.Remove () должны происходить в пределах блокировки, чтобы предотвратить изменение условий гонки в списке. Кроме того, ваши вызовы scheduleTask.Value.Invoke () должны происходить за пределами блокировки, чтобы не дать кому-либо поставить в очередь другой рабочий элемент в пределах этой блокировки, когда они реализуют Invoke. Я бы сделал это, переместив нужные задачи в список, локальный для стека, прежде чем покинуть блокировку, а затем выполняя задачи из этого списка.

Затем рассмотрим, что произойдет, если вызов Invoke () вызовет исключение, если в локальном списке есть оставшиеся задачи, они могут быть пропущены. Обработка может быть выполнена путем перехвата / проглатывания исключений или только извлечения 1 задачи из очереди при каждом снятии блокировки.

1 голос
/ 10 июня 2009

Поскольку вы не опубликовали код самого синглтона, трудно сказать, есть ли в этом проблемы. Но нет, пока объект доступен из статической переменной (или чего-либо в стеке), он не должен получать GC. Что означает, что ваша проблема ... что-то еще.

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

0 голосов
/ 10 июня 2009

Является ли ваше приложение обычной программой WinForms?

System.Windows.Forms.Timer прослушивает сообщения таймера в цикле сообщений вашей программы. Если ваша программа не является WinForms или если вы много работаете с потоком пользовательского интерфейса (что никогда не бывает хорошей идеей), вам, вероятно, следует использовать System.Timers.Timer. Обратите внимание, что он может запускать события в нескольких потоках; см здесь

0 голосов
/ 10 июня 2009

System.Windows.Forms.Timer не очень надежен в том смысле, что ему нравится получать GC в нечетное время.

Попробуйте вместо этого использовать System.Threading.Timer.

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