阅读时
How to: Create Pre-Computed Tasks
示例方法DownloadStringAsync
返回
Task.Run( async ()=> { return await new WebClient().DownloadStringTaskAsync(address)})
我想知道为什么我们需要在Task.Run()
中包装一个异步方法吗? WebClient().DownloadStringTaskAsync()
方法本身会返回Task
。
答案 0 :(得分:4)
我认为问题在于,他们想展示如何使用Task.FromResult
,然后因需要消耗Task
返回方法而陷入困境。
如今,编写消耗Task
返回方法的代码的自然方法是使它们成为async
。但是,如果这样做,Task.FromResult
就会消失:
// Asynchronously downloads the requested resource as a string.
public static async Task<string> DownloadStringAsync(string address)
{
// First try to retrieve the content from cache.
string content;
if (cachedDownloads.TryGetValue(address, out content))
{
return content;
}
content = await new WebClient().DownloadStringTaskAsync(address);
cachedDownloads.TryAdd(address, content);
return content;
}
简单的代码,仍然可以达到总体目标。除非您期望cachedDownloads.TryAdd
占用大量CPU资源,否则在这种情况下,其版本也会保证将其推送到线程池中运行。
简而言之-不要复制此代码,这不是从 2 开始工作的示例。
此版本可避免在不需要时避免分配async
状态机 1 ,显示Task.FromResult
并且仍然不使用Task.Run
: >
// Asynchronously downloads the requested resource as a string.
public static Task<string> DownloadStringAsync(string address)
{
// First try to retrieve the content from cache.
string content;
if (cachedDownloads.TryGetValue(address, out content))
{
return Task.FromResult(content);
}
return DownloadStringSlowAsync(address);
}
private static async Task<string> DownloadStringSlowAsync(string address)
{
string content = await new WebClient().DownloadStringTaskAsync(address);
cachedDownloads.TryAdd(address, content);
return content;
}
更好:(不,这不是一个字,我不在乎)
static ConcurrentDictionary<string, Task<string>> cachedDownloads =
new ConcurrentDictionary<string, Task<string>>();
// Asynchronously downloads the requested resource as a string.
public static Task<string> DownloadStringAsync(string address)
{
// First try to retrieve the content from cache.
Task<string> content;
if (cachedDownloads.TryGetValue(address, out content))
{
return content;
}
return DownloadStringSlowAsync(address);
}
private static async Task<string> DownloadStringSlowAsync(string address)
{
string content = await new WebClient().DownloadStringTaskAsync(address);
cachedDownloads.TryAdd(address, Task.FromResult(content));
return content;
}
因为现在我们的缓存仅包含完成的任务,我们可以一遍又一遍地分发它们,而不必在每个请求上重复分配新的Task
对象。
当然,只有当缓存的对象(此处为string
是不可变的时,这些方法中的任何一种才真正可行。
1 不要自动执行此操作。应该基于这样的分配是否引起绩效问题来进行深思熟虑的决定。
2 这也是 一个不好的缓存示例,因为正如Raymond Chen指出的那样,A cache with a bad policy is another name for a memory leak。在此示例中,根本没有 有效期。
答案 1 :(得分:2)
TL; DR不要使用该示例。除了作为智力锻炼。实际上,它的当前状态表明未完成,Robust Programming部分为空
长版
我们没有,这个例子是人为的。
实际的代码不是只是调用DownloadStringTaskAsync()
。实际的代码是:
static ConcurrentDictionary<string, string> cachedDownloads =
new ConcurrentDictionary<string, string>();
// Asynchronously downloads the requested resource as a string.
public static Task<string> DownloadStringAsync(string address)
{
// First try to retrieve the content from cache.
string content;
if (cachedDownloads.TryGetValue(address, out content))
{
return Task.FromResult<string>(content);
}
// If the result was not in the cache, download the
// string and add it to the cache.
return Task.Run(async () =>
{
content = await new WebClient().DownloadStringTaskAsync(address);
cachedDownloads.TryAdd(address, content);
return content;
});
}
这是一个非常具体的示例,需要返回一个保证结果已被缓存的任务。
该函数使HTTP调用与缓存异步。这意味着它必须返回一个任务。
首先,它检查结果是否已经可用。如果是,它将包裹在完整的Task
中:
string content;
if (cachedDownloads.TryGetValue(address, out content))
{
return Task.FromResult<string>(content);
}
否则,它将返回一个正在运行的任务,该任务会进行HTTP调用,并确保返回结果之前对其进行缓存:
// If the result was not in the cache, download the
// string and add it to the cache.
return Task.Run(async () =>
{
content = await new WebClient().DownloadStringTaskAsync(address);
cachedDownloads.TryAdd(address, content);
return content;
});
该文章有些虚构,但具有其他优点
本文试图说明如何使用已完成的任务,尽管该示例并没有那么清晰。它具有与主题没有直接关系的好处。
例如,他们避免使用async/await
并通过在最后返回正在运行的任务来避免这种花费。除非缓存中缺少结果,否则编译器将不生成async/await
方法所需的异步状态机。
这意味着更少的分配和更少的IL复杂度
但是随后他们去使用效率较低的WebClient
类...。