ContinueWith委托在Task完成之前运行

时间:2014-09-05 20:14:24

标签: c# concurrency task-parallel-library async-await concurrentdictionary

背景信息:我正在尝试创建一个能够一次处理5个并发操作的单例类。每个操作都由SomeAsyncMethod表示。


此方法位于单例类中。

consumersConcurrentDictionary<int,Task>

我的问题:由于某种原因,ContinueWith委托在SomeAsyncMethod完成之前运行。我知道这种情况发生的原因是因为我有另一种观察instance.Consumers.Count的方法 - 在0完成之前计数为SomeAsyncMethod

为什么?

    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;
    }

2 个答案:

答案 0 :(得分:2)

SomeAsyncMethod,如果它的名称是任何指示,则是异步方法,可能是返回Task的方法。您正在创建一个新任务,以便在另一个线程中启动此异步操作。完成启动异步操作后,Task将返回,而不是在它开始的异步操作完成时。{/ p>

虽然您可以解开任务,但更简单的选择就是不首先将其包装起来。在异步方法返回的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();
        }
    }
}

答案 1 :(得分:0)

由于SomeAsyncMethod是异步的,因此它会在完成自己的任务之前返回。

如果您可以控制SomeAsyncMethod的代码,那么将其重构为同步(无await / async),或者如果已存在非异步版本,则只需用那个。

如果你无法控制方法的代码,你可以等待它完成周围的任务,然后再继续:

Task newTask = new Task(()=>{ SomeAsyncMethod().Wait(); });