链接 async/await

Chaining async/await

我开发了许多算法,这些算法通过使用常规 Threads 自行完成大部分线程。该方法始终如下

float[] GetData(int requestedItemIndex)

使用上面的方法,索引被推入了一些消息队列,这些消息队列由独立算法的线程处理。所以最后算法的界面是这样的:

public abstract class AlgorithmBase
{
    private readonly AlgorithmBase Parent;

    private void RequestQueue()
    {
    }

    public float[] GetData(int requestedItemIndex) => Parent.GetData(requestedItemIndex);
}

这个例子很原始,但只是为了获得线索。问题是我可以链接当前适用于我的解决方案的算法。如您所见,每个 GetData 调用父算法的另一个 GetData。这当然会变得更复杂,当然需要有一个最终的父级作为数据源,否则我会得到 WhosebugExceptions。

现在我尝试使用 async/await 来改变这种行为。我的问题是,如果我重写我的代码,我会得到这样的结果:

  public abstract class AlgorithmBase
    {
        private readonly AlgorithmBase Parent;


        public async Task<float[]> GetDataAsync(int requestedItemIndex, CancellationToken token = default)
        {
            var data = await Parent.GetDataAsync(requestedItemIndex);

            return await Task.Run<float[]>(async () => ProcessData());
        }            
    }

现在,我已经链接了算法,每个新算法都跨越另一个任务,如果这样做多次,这可能会非常耗时。

所以我的问题是,是否有一种方法可以通过使用定义接口将下一个任务嵌入到已经 运行 的任务中?

不需要明确使用Task.Run。您应该避免这种情况,并将选择权留给 AlgorithmBase class 的消费者。

因此,您可以非常类似地实现 async 版本,其中 Task 对象将从父对象传播到子对象:

public abstract class AlgorithmBase
{
    private readonly AlgorithmBase Parent;

    private void RequestQueue()
    {
    }

    public Task<float[]> GetDataAsync(int requestedItemIndex) 
         => Parent.GetDataAsync(requestedItemIndex);
}

最终,一些 "parent" 将以与同步对应物相同的方式实现 GetDataAsync

public class SortAlgorithm : AlgorithmBase
{
    public override async Task<float[]> GetDataAsync(int requestedItemIndex)
    {
        // asynchronously get data 
        var data = await Parent.GetDataAsync(requestedItemIndex);

        // synchronously process data and return from asynchronous method
        return this.ProcessData(data);
    }

    private float[] ProcessData(float[] data)
    {
    }
}

最终,SortAlogirthm 的消费者可以决定是 await 它,还是干脆一劳永逸。

var algo = new SortAlgorithm();

// asynchronously wait until it's finished
var data = await algo.GetDataAsync(1);

// start processing without waiting for the result
algo.GetDataAsync(1);

// not needed - GetDataAsync already returns Task, Task.Run is not needed in this case
Task.Run(() => algo.GetDataAsync(1));

在库代码中等待时,您通常希望在所有 await each and every time, especially if you are awaiting in a loop. So to improve the performance of your library consider using .ConfigureAwait(false)