繁体   English   中英

顺序异步任务队列不等到异步任务结束

[英]Sequential async task queue does not wait until async task ends

我试图按顺序将异步任务排队,所以为此做了一个类:

 public class TaskSequentialQueue : IDisposable, ITaskSequentialQueue
    {
        public delegate void OnExeptionDelegate(Exception ex);

        private readonly Queue<Task> m_queue = new Queue<Task>();
        private readonly Object m_lock = new Object();
        private readonly CancellationTokenSource m_CancelToken = new CancellationTokenSource();
        private readonly OnExeptionDelegate m_onExeptionDelegate = null;
        private Task m_currentTask = null;
        private bool m_isDisposed = false;      

        public TaskSequentialQueue(OnExeptionDelegate expDelegate = null)
        {
            m_onExeptionDelegate = expDelegate;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool isDisposing)
        {
            if (m_isDisposed)
            {
                return;
            }

            if (isDisposing)
            {
                lock (m_lock)
                {
                    m_isDisposed = true;
                    m_queue.Clear();
                }

                m_CancelToken.Cancel();
                m_CancelToken.Dispose();
            }
        }

        public void EnqueueTask( Task task)
        {
            lock (m_lock)
            {
                if (m_isDisposed)
                    throw new ObjectDisposedException("TaskSequentialQueue");

                m_queue.Enqueue(task);
            }

            StartNextTask();
        }

        public void EnqueueTask( Func<Task> task)
        {
            EnqueueTask(new Task<Task>(task));
        }

        public Task EnqueueTaskAndWait( Task task)
        {
            TaskCompletionSource<int> taskSource = new TaskCompletionSource<int>();
            lock (m_lock)
            {
                if (m_isDisposed)
                    throw new ObjectDisposedException("TaskSequentialQueue");

                Func<Task> toDo = async () =>
                {                                        
                    var waitabletask = task.ContinueWith( antecedent => 
                                                            { 
                                                                taskSource.SetResult(0); 
                                                                if (antecedent.Exception != null)
                                                                    throw antecedent.Exception; 
                                                            });
                    task.Start();
                    await waitabletask;
                };
                this.EnqueueTask(toDo);
            }

            StartNextTask();

            return taskSource.Task;  //TODO! propagate the exception correctly ?
        }

        private void StartNextTask()
        {
            Task theTask = null;
            lock(m_lock)
            {
                if (m_currentTask == null && m_queue.Count > 0 && !m_isDisposed)
                {
                    m_currentTask = m_queue.Dequeue();
                    theTask = m_currentTask;
                }
            }      
            if (theTask != null)
            {
                theTask.Start();  
                theTask.ContinueWith( (antecedent) =>
                    {
                        Exception theEx = antecedent.Exception;
                        if (theEx == null && antecedent is Task<Task>)
                            theEx = (antecedent as Task<Task>)?.Result.Exception;

                        if (m_onExeptionDelegate != null && theEx != null)
                        {
                            try { m_onExeptionDelegate(theEx); } catch(Exception) {}
                        }

                        lock(m_lock)
                        {
                            m_currentTask = null;
                        }

                        Task.Run( () =>  StartNextTask() );
                }
            }
        }
    }

我这样使用它:

     Func<Task> action = async () =>
                {
                   Log("Entered");
                   await Task.Delay(5000);
                   Log("Exited");
                }

m_taskSequentialQueue.EnqueueTask( action );
m_taskSequentialQueue.EnqueueTask( action );

我希望我的日志显示为:

Entered
Exited
Entered
Exited

相反,我得到:

Entered
Entered
Exited
Exited

我不确定自己在做什么错。

谢谢

当你theTask.ContinueWith(StartNextTask你继续在事情是内部任务内的任务没有完成的启动。一旦内任务到达第一个awaittheTask任务将被视为完成,因为该函数返回。

作为创可贴,你可以做

if (theTask != null)
{
    theTask.Start();  
    if(theTask is Task<Task>)
    {
       theTask = ((Task<Task>)theTask).Unwrap();
    }
    theTask.ContinueWith(...

但是,我认为您使用“冷任务”的整个方法都是有缺陷的。 您应该只使用Queue<Func<Task>>而不是Queue<Task> ,它将使您的代码更加简单。

        Func<Task> action = async () =>
        {
            lock (lock_x)
            {
                Console.WriteLine("Entered");
                Thread.Sleep(5000);
                Console.WriteLine("Exited");
            }
        };

应该可以正常工作,因为您的队列实现是按顺序提交它们的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM