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

时间:2016-06-04 07:02:13

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

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

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

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

3 个答案:

答案 0 :(得分:11)

此:

ForumPosts

创建一个延迟评估的IEnumerable,它将数字映射到方法调用结果。此处未调用var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i)); ,在评估期间将 调用。

此IEnumerable被评估两次。这里:

ReadSensorsAsync

在这里:

await Task.WhenAll(tasksRead);

因此,// Here, another lazy IEnumerable is created based on tasksRead. var tasksRecord = tasksRead.Where(...).Select(...); await Task.WhenAll(tasksRecord); // Here, it is evaluated. 被调用两次。

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

ReadSensorsAsync

答案 1 :(得分:2)

Task.WhenAll上使用IEnumerable<Task<T>>时,它将返回T[]个已完成的任务结果。您需要保存该变量并使用它,否则最终会得到Henzi mentioned in his answer等多个枚举。

这是一个没有不必要地调用.ToList()

的解决方案
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));
        var finshedTasks = await Task.WhenAll(tasksRead);
        var tasksRecord = finshedTasks.Where(x => x != null).Select(x => RecordReadingAsync(x));
        await Task.WhenAll(tasksRecord);

        Interlocked.Decrement(ref currentlyRunningTasksCount);
}

答案 2 :(得分:0)

我想我知道为什么! 用两个词来说: - 使用await impliciti的函数创建回调线程的原因。更好的是你可以看到Jeffrey Richter在00:17:25

的视频https://wintellectnow.com/Videos/Watch?videoId=performing-i-o-bound-asynchronous-operations上解释它

试一试:

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

await Task.WhenAll(tasksRead);

await Task.WhenAll(tasksRecord);