ContinueWith委托在任务完成之前运行

本文关键字:运行 任务 ContinueWith | 更新日期: 2023-09-27 18:20:45

背景信息:我正在尝试创建一个能够同时处理5个并发操作的singleton类。每个操作由CCD_ 1表示。


此方法位于singleton类中。

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

ContinueWith委托在任务完成之前运行

SomeAsyncMethod,如果它的名称有任何指示的话,它是一个异步方法,可能是一个返回SomeAsyncMethod0的方法。您正在创建一个新任务以在另一个线程中启动此异步操作。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(); });