Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 可暂停&;可恢复异步任务队列_C#_.net_Async Await_Task Queue_Cancellation - Fatal编程技术网

C# 可暂停&;可恢复异步任务队列

C# 可暂停&;可恢复异步任务队列,c#,.net,async-await,task-queue,cancellation,C#,.net,Async Await,Task Queue,Cancellation,我已经基于我在这里找到的东西实现了一个工作队列>…但是在实现附加功能时遇到了麻烦 我取出了Func,用icommand(持有自己的取消令牌)替换了它们,并打算添加Pause(),Resume(),Save()和Restore()方法。因此,OnFormClose()我可以暂停队列处理,并提示用户决定是“等待队列完成”(即恢复),还是“立即退出”(即保存并退出) 公共类WqController { 私有只读队列_Queue=new Queue(); 专用任务队列处理器; 专用ICommandu c

我已经基于我在这里找到的东西实现了一个工作队列>…但是在实现附加功能时遇到了麻烦

我取出了
Func
,用icommand(持有自己的取消令牌)替换了它们,并打算添加
Pause()
Resume()
Save()
Restore()
方法。因此,
OnFormClose()
我可以暂停队列处理,并提示用户决定是“等待队列完成”(即恢复),还是“立即退出”(即保存并退出)

公共类WqController
{
私有只读队列_Queue=new Queue();
专用任务队列处理器;
专用ICommandu curCommand;
公共无效排队(ICommand命令)
{
_queue.Enqueue(命令);
如果(_queueProcessor==null)_queueProcessor=ProcessQueue();
}
专用异步任务ProcessQueue()
{
尝试
{
而(_queue.Count!=0)
{
_curCommand=_queue.Peek();
尝试
{
等待任务。运行(()=>\u curCommand.Execute());
}
捕获(操作取消异常)
{
控制台写入线(“队列暂停”);
返回;
}
捕获(例外)
{
Console.WriteLine(“无法执行命令”);
}
_queue.Dequeue();
}
}
最后
{
_queueProcessor=null;
_curCommand=null;
}
}
公共异步任务取消()
{
_curCommand.Cts.Cancel(true);
等待队列处理器;
}
公众简历()
{
_queueProcessor=ProcessQueue();
}
}

Save()
Restore()
工作正常,所以我没有在这里包括它们。
Cancel()
间歇/不可靠地工作,而
Restore()
似乎根本不起作用(让我困惑的是,我基本上是在尝试与
Enqueue()
方法相同的重新启动)。

我已经开始工作了,我认为应该在这里概述我的解决方案

事实证明,我对取消令牌的使用有点随意,这妨碍了该类按预期运行。例如,以下问题是相关的:

  • 如果在命令中通过最后一次取消检查后调用了Cancel,则将加载一个新命令(连同它自己的新取消令牌),因此Cancel调用将丢失/忽略。这可以通过
    if(_curCommand.Cts.Token.iscancellationrequest)返回来解决紧跟在
    \u queue.Dequeue()之后

  • 在调用cancel之后,如果稍后要恢复该命令,那么它将需要一个新的取消令牌(否则,cancel=true的现有令牌仍将处于活动状态)。行
    \u 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();
        }
    }
    
    公共类WqController
    {
    私有只读队列_Queue=new Queue();
    专用任务队列处理器;
    专用ICommandu curCommand;
    公共无效排队(ICommand命令)
    {
    _queue.Enqueue(命令);
    如果(_queueProcessor==null)_queueProcessor=ProcessQueue();
    }
    专用异步任务ProcessQueue()
    {
    尝试
    {
    而(_queue.Count!=0)
    {
    _curCommand=_queue.Peek();
    尝试
    {
    等待任务。运行(()=>\u curCommand.Execute());
    }
    捕获(操作取消异常)
    {
    _curCommand.InvalidateCancellationToken();
    控制台写入线(“队列暂停”);
    返回;
    }
    捕获(例外)
    {
    Console.WriteLine(“无法执行命令”);
    }
    _queue.Dequeue();
    if(_curCommand.Cts.Token.IsCancellationRequested)返回;
    }
    }
    最后
    {
    _queueProcessor=null;
    _curCommand=null;
    }
    }
    公共异步任务取消()
    {
    _curCommand.Cts.Cancel(true);
    等待队列处理器;
    }
    公众简历()
    {
    _queueProcessor=ProcessQueue();
    }
    }
    

    这一切现在似乎都运行得非常顺利,这是对我以前使用的后台工作队列实现的一个重大改进。

    很难看出这个类如何保持任何不变量。例如,如果
    状态!=WqStatus.暂停
    然后恢复
    只是默默地失败。添加断言,确保仅当状态为“暂停”
    时才调用“恢复”。另外,您假定
    \u queueProcessor==null
    。也为它添加一个断言。诸如此类。我认为我所看到的问题与地位或数量无关。恢复代码肯定正在运行。我想我会在这方面调整代码。当我打开
    Resume()
    时,
    \u queueProcessor
    的状态为“WaitingForActivation”,如果这有帮助的话?在我看来,我们缺少确定发生了什么的代码。例如,您谈到取消,但您在代码中真正检查您的
    CancellationToken
    ?我看不到这一部分。@YuvalItzchakov:我正在执行一个
    \u curCommand.Cts.Token.ThrowIfCancellationRequested()
    ,就在通过此队列的命令调用的任何代码中的任何长时间运行的任务之前。您的评论刚刚帮助我解释了
    Cancel()
    的间歇性。如果在最后一次取消后调用cancel c
    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();
        }
    }