我使用线程计时器做一些定期工作:
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
以某种方式让它运行两次吗?
答案 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);