这是我想要做的一个愚蠢的版本:
private static int Inc(int input)
{
return input + 1;
}
private static async Task<int> IncAsync(int input)
{
await Task.Delay(200);
return input + 1;
}
private static async Task<IEnumerable<TResult>> GetResultsAsync<TInput, TResult>(Func<TInput, TResult> func, IEnumerable<TInput> values)
{
var tasks = values.Select(value => Task.Run(() => func(value)))
.ToList();
await Task.WhenAll(tasks);
return tasks.Select(t => t.Result);
}
public async void TestAsyncStuff()
{
var numbers = new[] { 1, 2, 3, 4 };
var resultSync = await GetResultsAsync(Inc, numbers); // returns IEnumerable<int>
Console.WriteLine(string.Join(",", resultSync.Select(n => $"{n}")));
// The next line is the important one:
var resultAsync = await GetResultsAsync(IncAsync, numbers); // returns IEnumerable<Task<int>>
}
基本上,GetResultsAsync()
旨在成为一个通用方法,它将获得一组输入值的函数结果。在TestAsyncStuff()
中,您可以看到它如何用于调用同步函数(Inc()
)。
当我想调用异步函数(IncAsync()
)时出现问题。我得到的结果是IEnumerable<Task<int>>
类型。我可以对该结果进行Task.WhenAll()
,这有效:
var tasksAsync = (await GetResultsAsync(IncAsync, numbers)).ToList();
await Task.WhenAll(tasksAsync);
var resultAsync = tasksAsync.Select(t => t.Result);
Console.WriteLine(string.Join(",", resultAsync.Select(n => $"{n}")));
但我想收紧代码并执行await
内联。看起来应该是这样的:
var resultAsync = await GetResultsAsync(async n => await IncAsync(n), numbers);
但是这也会返回IEnumerable<Task<int>>
!我能做到这一点:
var resultAsync = await GetResultsAsync(n => IncAsync(n).GetAwaiter().GetResult(), numbers);
这有效......但从我看到的情况来看,不鼓励使用Task.GetAwaiter().GetResult()
或Task.Result
。
那么这样做的正确方法是什么?
答案 0 :(得分:3)
您应该创建两个GetResultsAsync
的重载。一个人应该接受一个同步的&#39;委托,返回TResult
。此方法将每个委托包装到一个任务中,并异步运行它们:
private static async Task<IEnumerable<TResult>> GetResultsAsync<TInput, TResult>(
Func<TInput, TResult> func, IEnumerable<TInput> values)
{
var tasks = values.Select(value => Task.Run(() => func(value)));
return await Task.WhenAll(tasks);
}
第二次重载将接受异步&#39;委托,返回Task<TResult>
。此方法不需要将每个委托包装到任务中,因为它们已经是任务:
private static async Task<IEnumerable<TResult>> GetResultsAsync<TInput, TResult>(
Func<TInput, Task<TResult>> func, IEnumerable<TInput> values)
{
var tasks = values.Select(value => func(value));
return await Task.WhenAll(tasks);
}
您甚至可以从第一个方法调用第二个方法以避免代码重复:
private static async Task<IEnumerable<TResult>> GetResultsAsync<TInput, TResult>(
Func<TInput, TResult> func, IEnumerable<TInput> values)
{
return await GetResultsAsync(x => Task.Run(() => func(x)), values);
}
注意:这些方法不会简化您的生活。
可以达到相同的效果var resultSync = await Task.WhenAll(numbers.Select(x => Task.Run(() => Inc(x))));
var resultAsync = await Task.WhenAll(numbers.Select(IncAsync));
答案 1 :(得分:0)
我说你关心的是一种风格:你想要的东西看起来更好。对于您的第一个案例,请考虑:
<html>
<head>
<script type="text/javascript">
function myFunc(){
alert(document.getElementById( 'frame' ).contentWindow.document.body.innerText);
}
</script>
</head>
<body>
<div id="list">
<p>
<iframe src="x.txt" id="frame" width="50" height="50" onload="myFunc()"></iframe>
</p>
</div>
</body>
</html>
理由是Plinq已经做了你想要做的事情:它并行var resultSync= numbers.AsParallel()/*.AsOrdered()*/.Select(Inc);
。对于您的第二种情况,在IEnumerables
周围创建Tasks
毫无意义。相当于:
Tasks
但我更喜欢谢尔盖的var resultAsync = numbers.AsParallel()./*AsOrdered().*/Select(n => IncAsync(n).Result);
。
也许我真正喜欢的是一对Linq风格的重载:
await Task.WhenAll(numbers.Select(IncAsync))
顺便说一句,如果您将var numbers = Enumerable.Range(1,6);
var resultSync = await Enumerable.Range(1,6).SelectAsync(Inc);
var resultAsync = await Enumerable.Range(1,100).SelectAsync(IncAsync);
Console.WriteLine("sync" + string.Join(",", resultSync));
Console.WriteLine("async" + string.Join(",", resultAsync));
static class IEnumerableTasks
{
public static Task<TResult[]> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> func)
{
return Task.WhenAll( source.Select(async n => await Task.Run(()=> func(n))));
}
public static Task<TResult[]> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> func)
{
return Task.WhenAll(source.Select(func));
}
}
static int Inc(int input)
{
Task.Delay(1000).Wait();
return input+1;
}
static async Task<int> IncAsync(int input)
{
await Task.Delay(1000);
return input + 1;
}
更改为Range(1,6)
,则会显示异步的优势。在我的机器上,即使对于Range(1,40)