ContinueWith委托在任务完成之前运行
本文关键字:运行 任务 ContinueWith | 更新日期: 2023-09-27 18:20:45
背景信息:我正在尝试创建一个能够同时处理5个并发操作的singleton类。每个操作由CCD_ 1表示。
此方法位于singleton类中。
consumers
是ConcurrentDictionary<int,Task>
我的问题是:由于某种原因,ContinueWith
代理在SomeAsyncMethod
完成之前运行。我之所以知道会发生这种情况,是因为我有另一个监视instance.Consumers.Count
的方法——在SomeAsyncMethod
完成之前,计数是0
。
为什么?
public bool TryAddDequeueRequest()
{
if (instance.Consumers.Count < 5)
{
Task bogusTask;
Task newTask = new Task(SomeAsyncMethod);
//RUNS AFTER THE REQUEST IS COMPLETED
newTask.ContinueWith(t =>
{
instance.Consumers.TryRemove(t.Id, out bogusTask);
});
//WE ADD THE TASK TO QUEUE
instance.Consumers.TryAdd(newTask.Id, newTask);
//SET IT AND FORGET IT
newTask.Start();
return true;
}
else
return false;
}
SomeAsyncMethod
,如果它的名称有任何指示的话,它是一个异步方法,可能是一个返回SomeAsyncMethod
0的方法。您正在创建一个新任务以在另一个线程中启动此异步操作。Task
将在您完成启动异步操作时返回,而不是在它启动的异步操作完成时返回。
虽然您可以打开任务包装,但更简单的选择是首先不要包装它。调用异步方法返回的Task
上的continuation:
SomeAsyncMethod().ContinueWith(t =>
{
instance.Consumers.TryRemove(t.Id, out bogusTask);
});
instance.Consumers.TryAdd(newTask.Id, newTask);
当然,如果您希望能够以固定的并行度执行一定数量的异步操作,那么有更简单的方法。您可以使用SemaphoreSlim
来创建一个具有任何固定并行度的工作队列:
public class FixedParallelismQueue
{
private SemaphoreSlim semaphore;
public FixedParallelismQueue(int maxDegreesOfParallelism)
{
semaphore = new SemaphoreSlim(maxDegreesOfParallelism);
}
public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
{
await semaphore.WaitAsync();
try
{
return await taskGenerator();
}
finally
{
semaphore.Release();
}
}
public async Task Enqueue(Func<Task> taskGenerator)
{
await semaphore.WaitAsync();
try
{
await taskGenerator();
}
finally
{
semaphore.Release();
}
}
}
由于SomeAsyncMethod
是异步的,它在完成自己的任务之前返回。
如果您可以控制SomeAsyncMethod
的代码,那么将其重构为同步的(没有await
/async
),或者如果已经有非异步版本,那么就使用它。
如果你不能控制方法的代码,你可以在继续之前等待它在周围的任务中完成:
Task newTask = new Task(()=>{ SomeAsyncMethod().Wait(); });