我有一种使用async task
执行长动作的方法
现在,我想添加一个在同一方法中透明的缓存机制。
现在,我总是可以获取我的缓存结果并使用Task包装它以便它“工作”但我想阻止我将获得的上下文切换。
以下是我所拥有的一个例子:
var result = await LongTask();
private async Task<string> LongTask()
{
return await DoSomethingLong();
}
这是我想要的一个例子:
var result = await LongTask();
private async Task<string> LongTask()
{
if(isInCache)
{
return cachedValue(); // cache value is a const string you can do return "1" instead.
}
// else do the long thing and return my Task<string>
return await DoSomethingLong();
}
现在我很惊讶地看到这个编译和工作了 有些东西告诉我,我不正确地做到了。
这是我测试过的另一个类似的例子:
private async Task<string> DownloadString(bool sync)
{
using (WebClient wc = new WebClient())
{
var task = wc.DownloadStringTaskAsync("http://www.nba.com");
if(sync)
return task.Result;
return await task;
}
}
这是代码:
var res = DownloadString(true);
string str1 = await res;
var res2 = DownloadString(false);
string str2 = await res2;
从我读过的内容here task.Result
同步执行任务并返回string
。
现在我通过Fiddler看到了请求,我的程序被卡在return task.Result
行,即使我看到200 OK
并且我等了很长时间。
底线:
context switch overhead
?DownloadString
被卡住了? 答案 0 :(得分:6)
首先,如果在调用async
方法之后返回的任务已经完成,则不会有上下文切换,因为不需要任何上下文切换。所以这是完全可以接受的:
private async Task<string> LongTask()
{
if(isInCache)
{
return cachedValue(); // cache value is a const string you can do return "1" instead.
}
// else do the long thing and return my Task<string>
return await DoSomethingLong();
}
但是,在缓存结果的情况下,async
机制是多余的。此开销几乎可以忽略不计,但您可以通过删除async
和await
并使用Task.FromResult
创建已完成的任务来提高性能:
private Task<string> LongTask()
{
if(isInCache)
{
return Task.FromResult(cachedValue());
}
// else do the long thing and return my Task<string>
return DoSomethingLong();
}
...当您编写“await someObject;”时,编译器将生成检查someObject表示的操作是否已完成的代码。如果有,则执行在等待点上同步继续。如果没有,生成的代码将连续委托连接到等待的对象,这样当表示的操作完成时,将调用该连续委托
Task.Result
没有同步执行任务,它会同步等待。这意味着调用线程被阻塞,等待任务完成。当您在SynchronizationContext
的环境中使用它时可能会导致死锁,因为线程被阻止且无法完成任务。您不应该对尚未完成的任务使用Result
属性。