[英]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
你继续在事情是内部任务内的任务没有完成的启动。一旦内任务到达第一个await
的theTask
任务将被视为完成,因为该函数返回。
作为创可贴,你可以做
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.