2014-11-23 6 views
1

Я выполнил рабочую очередь на основе того, что я нашел здесь>Task queue for wp8? ... но у меня возникли проблемы с реализацией с ней дополнительных функций.Pausable & resumable async task queue

я выломали Func<Task> «S и заменил их ICommands (проведение своих собственных CancellationTokens), и предназначен для добавления Pause(), Resume(), Save() & Restore() методы. Это так OnFormClose() Я могу приостановить обработку очереди и предложить пользователю решить, хочет ли он «ждать окончания очереди» (т. Е. Возобновить) или «выйти сейчас» (т. Е. Сохранить и выйти).

public class WqController 
{ 
    private readonly Queue<ICommand> _queue = new Queue<ICommand>(); 
    private Task _queueProcessor; 
    private ICommand _curCommand; 

    public void Enqueue(ICommand command) 
    { 
     _queue.Enqueue(command); 

     if (_queueProcessor == null) _queueProcessor = ProcessQueue(); 
    } 

    private async Task ProcessQueue() 
    { 
     try 
     { 
      while (_queue.Count != 0) 
      { 
       _curCommand = _queue.Peek(); 

       try 
       { 
        await Task.Run(() => _curCommand.Execute()); 
       } 
       catch (OperationCanceledException) 
       { 
        Console.WriteLine("QUEUE PAUSED"); 
        return; 
       } 
       catch (Exception) 
       { 
        Console.WriteLine("FAILED TO EXECUTE COMMAND"); 
       } 
       _queue.Dequeue(); 
      } 
     } 
     finally 
     { 
      _queueProcessor = null; 
      _curCommand = null; 
     } 
    } 

    public async Task Cancel() 
    { 
     _curCommand.Cts.Cancel(true); 
     await _queueProcessor; 
    } 

    public void Resume() 
    { 
     _queueProcessor = ProcessQueue(); 
    } 
} 

Save() & Restore() отлично работает, так что я не включил их здесь. Cancel() работает с перерывами/нерегулярно, и Restore(), похоже, не работает вообще (смехотворно для меня, поскольку я в основном пытаюсь просто перезапустить, как работает в методе Enqueue()).

+0

Трудно понять, как этот класс выдерживает любые инварианты. Например, если 'Status! = WqStatus.Paused', то' Resume' просто молчал. Добавьте утверждения, убедившись, что 'Resume' вызывается только в том случае, если состояние« приостановлено ». Кроме того, вы предполагаете, что '_queueProcessor == null'. Добавьте утверждение для этого. И так далее. – usr

+0

Я не * думаю *, что статус или граф вовлечены в проблему, которую я вижу. Код резюме определенно работает. Подумайте, что я настрою код в этом отношении. – FrugalTPH

+0

Когда я прерываю 'Resume()', статус '_queueProcessor' говорит« WaitingForActivation », если это какая-то помощь? – FrugalTPH

ответ

0

Я получил эту работу и подумал, что должен изложить свое решение здесь.

Оказывается, мое использование токенов отмены было немного случайным, что мешало этому классу функционировать по назначению. Например, следующие вопросы были актуальны:

  1. Если Отменить был вызван после того, как последняя проверка отмены прошло в команде, новая команда будет загружена (вместе со своим самым новым отмены), и как таковой вызов отмены был бы потерян/проигнорирован. Это было решено с if (_curCommand.Cts.Token.IsCancellationRequested) return; сразу после _queue.Dequeue();.

  2. После того, как была вызвана отмена, если команда должна была быть продолжена позже, тогда ей понадобится новый токен отмены (иначе существующий с cancel = true все равно будет активным). Строка _curCommand.InvalidateCancellationToken(); делает это, устанавливая токен в значение null, а затем моя команда обновляет токен при следующем вызове.

Полный код, который я использовал:

public class WqController 
{ 
    private readonly Queue<ICommand> _queue = new Queue<ICommand>(); 
    private Task _queueProcessor; 
    private ICommand _curCommand; 

    public void Enqueue(ICommand command) 
    { 
     _queue.Enqueue(command); 

     if (_queueProcessor == null) _queueProcessor = ProcessQueue(); 
    } 

    private async Task ProcessQueue() 
    { 
     try 
     { 
      while (_queue.Count != 0) 
      { 
       _curCommand = _queue.Peek(); 

       try 
       { 
        await Task.Run(() => _curCommand.Execute()); 
       } 
       catch (OperationCanceledException) 
       { 
        _curCommand.InvalidateCancellationToken(); 
        Console.WriteLine("QUEUE PAUSED"); 
        return; 
       } 
       catch (Exception) 
       { 
        Console.WriteLine("FAILED TO EXECUTE COMMAND"); 
       } 
       _queue.Dequeue(); 
       if (_curCommand.Cts.Token.IsCancellationRequested) return; 
      } 
     } 
     finally 
     { 
      _queueProcessor = null; 
      _curCommand = null; 
     } 
    } 

    public async Task Cancel() 
    { 
     _curCommand.Cts.Cancel(true); 
     await _queueProcessor; 
    } 

    public void Resume() 
    { 
     _queueProcessor = ProcessQueue(); 
    } 
} 

Все это, кажется, работает очень гладко сейчас, и это большой шаг вперед по реализации фона очереди работника я использовал ранее.