为什么异步函数被调用两次?

Pab*_*blo 5 c# task-parallel-library async-await

我正在使用线程计时器来做一些定期工作:

private static async void TimerCallback(object state)
{
        if (Interlocked.CompareExchange(ref currentlyRunningTasksCount, 1, 0) != 0)
        {
            return;
        }

        var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i));
        await Task.WhenAll(tasksRead);
        var tasksRecord = tasksRead.Where(x => x.Result != null).Select(x => RecordReadingAsync(x.Result));
        await Task.WhenAll(tasksRecord);

        Interlocked.Decrement(ref currentlyRunningTasksCount);
}
Run Code Online (Sandbox Code Playgroud)

我打电话给定时器async并使用了WhenAll.在每个工作异步功能中,我有一个控制台输出,显示活动.现在的问题是,在第二个定时器事件中,每个异步函数由于某种原因正在工作两次.计时器设置为长时间.该应用程序是Windows控制台类型.是不是Select以某种方式使它运行两次?

Hei*_*nzi 11

这个:

var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i));
Run Code Online (Sandbox Code Playgroud)

创建一个延迟评估的IEnumerable,它将数字映射到方法调用结果.ReadSensorsAsync这里没有调用时,它评估过程中调用.

此IEnumerable被评估两次.这里:

await Task.WhenAll(tasksRead);
Run Code Online (Sandbox Code Playgroud)

和这里:

// Here, another lazy IEnumerable is created based on tasksRead.
var tasksRecord = tasksRead.Where(...).Select(...);  
await Task.WhenAll(tasksRecord);  // Here, it is evaluated.
Run Code Online (Sandbox Code Playgroud)

因此,ReadSensorsAsync被调用两次.


正如csharpfolk在评论中建议的那样,实现IEnumerable应该解决这个问题:

var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i)).ToList();
Run Code Online (Sandbox Code Playgroud)

  • 而不是使用`.ToList()`,这也可以通过使用由`await Task.WhenAll(tasksRead);`返回的`T []`来解决.以下`.Where(`而不是使用`tasksRead`第二次.另外,从技术上讲,它使用的第二个位置不是`tasksRead.Where(`它是`await Task.WhenAll(tasksRecord);`因为`.Where(`懒得评价,就像第一部分. (2认同)