Производитель-потребитель с вариантом - как синхронизировать с потоком сигнала / ждать?

StackOverflow https://stackoverflow.com/questions/4261283

Вопрос

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

ThreadPool.QueueUserWorkItem (() => 
{
    Thread.Sleep (5000);
    Foo (); // Call is to be executed after sometime
});

Итак, я создал отдельный класс планировщика, который работает на свой поток и выполняет эти события. У меня есть 2 функции, которые получают доступ к общей очереди от отдельных потоков. Я бы использовал замок, но поскольку один из ниток должен спать - подождите, я не был уверен, как освободить замок.

class Scheduler
{
    SortedDictionary <DateTime, Action> _queue;
    EventWaitHandle _sync;

    // Runs on its own thread
    void Run ()
    {
        while (true)
        {
            // Calculate time till first event
            // If queue empty, use pre-defined value
            TimeSpan timeDiff = _queue.First().Key - DateTime.Now;

            // Execute action if in the next 100ms
            if (timeDiff < 100ms)
                ...
            // Wait on event handle for time
            else
                _sync.WaitOne (timeDiff);
        }
    }

    // Can be called by any thread
    void ScheduleEvent (Action action, DataTime time)
    {
        _queue.Add (time, action);
        // Signal thread to wake up and check again
        _sync.Set ();
    }
}

  • Беда в том, что я не уверен, как синхронизировать доступ к очереди между 2 функциями. Я не могу использовать монитор или Mutex, потому что Run () будет спать-ждать, что вызывает тупик. Что такое правильный механизм синхронизации здесь? (Если есть механизм, чтобы аторично начать процесс ожидания сна и немедленно отпустите замок, которые могут решить мою проблему)
  • Как я могу проверить, нет гонки-состояния?
  • Это вариация проблемы потребительских производителей, или есть ли более актуальная проблема синхронизации - описание?

    Хотя это несколько ориентировано на C #, я был бы рад услышать общее решение этого. Спасибо!

  • Это было полезно?

    Решение

    ОК, возьмите 2 с монитором / пульсом.

        void Run ()    
        {
            while (true)
            {
                Action doit = null;
    
                lock(_queueLock)
                {
                    while (_queue.IsEmpty())
                        Monitor.Wait(_queueLock);
    
                    TimeSpan timeDiff = _queue.First().Key - DateTime.Now;
                    if (timeDiff < 100ms)
                        doit = _queue.Dequeue();
                }
    
                if (doit != null)
                    ; //execute doit
                else
                 _sync.WaitOne (timeDiff);  
            }
        }
    
    
    void ScheduleEvent (Action action, DataTime time)
    {
        lock (_queueLock)
        {
            _queue.Add(time, action);
            // Signal thread to wake up and check again
            _sync.Set ();
            if (_queue.Count == 1)
                 Monitor.Pulse(_queuLock);
        }
    }
    

    Другие советы

    Проблема легко решается, убедитесь, что ожидается выходная информация замка.

      //untested
      while (true)
      {
          Action doit = null;
    
          // Calculate time till first event
          // If queue empty, use pre-defined value
          lock(_queueLock)
          {
             TimeSpan timeDiff = _queue.First().Key - DateTime.Now;
             if (timeDiff < 100ms)
                doit = _queue.Dequeue();
          }
          if (doit != null)
            // execute it
          else
             _sync.WaitOne (timeDiff);
      }
    

    _QUELOCK - это частный помощник объекта.

    Поскольку ваша цель - запланировать задачу после определенного периода времени, почему бы не просто использовать System.Threading.timer? Это не требует посвящения нити для планирования и использует ОС, чтобы разбудить рабочую нить. Я использовал это (удалил некоторые комментарии и другие функции службы таймера):

    public sealed class TimerService : ITimerService
    {
        public void WhenElapsed(TimeSpan duration, Action callback)
        {
            if (callback == null) throw new ArgumentNullException("callback");
    
            //Set up state to allow cleanup after timer completes
            var timerState = new TimerState(callback);
            var timer = new Timer(OnTimerElapsed, timerState, Timeout.Infinite, Timeout.Infinite);
            timerState.Timer = timer;
    
            //Start the timer
            timer.Change((int) duration.TotalMilliseconds, Timeout.Infinite);
        }
    
        private void OnTimerElapsed(Object state)
        {
            var timerState = (TimerState)state;
            timerState.Timer.Dispose();
            timerState.Callback();
        }
    
        private class TimerState
        {
            public Timer Timer { get; set; }
    
            public Action Callback { get; private set; }
    
            public TimerState(Action callback)
            {
                Callback = callback;
            }
        }
    }
    

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

        void Run()
        {
          while(true)
             lock(this)
             {
                int timeToSleep = getTimeToSleep() //check your list and return a value
                if(timeToSleep <= 100) 
                    action...
                else
                {
    
                   int currTime = Datetime.Now;
                   int currCount = yourList.Count;
                   try{
                   do{
                     Monitor.Wait(this,timeToSleep);
    
                     if(Datetime.now >= (tomeToSleep + currtime))
                          break; //time passed
    
                     else if(yourList.Count != currCount)
                        break; //new element added go check it
                     currTime = Datetime.Now;
                   }while(true);
                }
                }catch(ThreadInterruptedException e)
                {
                    //do cleanup code or check for shutdown notification
                }
             }
          }
        }
    
    void ScheduleEvent (Action action, DataTime time)
    {
        lock(this)
        {
           yourlist.add ...
           Monitor.Pulse(this);
    

    } }

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top