将同步缓存机制添加到异步方法“;透明”;
本文关键字:透明 异步方法 同步 缓存 机制 添加 | 更新日期: 2023-09-27 18:27:06
我有一个方法,它使用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;
根据我在这里读到的内容,task.Result
同步执行任务并返回一个string
。现在,我通过Fiddler看到了请求,我的程序卡在了return task.Result
行,尽管我看到了200 OK
,我等待了很长时间。
底线:
- 在异步方法中使用缓存的最佳''正确方式是什么(例如,在某些情况下同步执行某些操作而不创建
context switch overhead
? - 为什么我使用
DownloadString
的第二个代码块被卡住了
首先,如果在调用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();
}
当您编写"wait-someObject;"时,编译器将生成代码来检查someObject表示的操作是否已经完成。如果有,执行将在等待点上同步继续。如果没有,生成的代码将把一个延续委托挂接到等待的对象,这样当所表示的操作完成时,该延续委托将被调用
来自异步/等待常见问题
Task.Result
不同步执行任务,而是同步等待。这意味着调用线程被阻塞,等待任务完成。当您在具有SynchronizationContext
的环境中使用它时,可能会导致死锁,因为线程被阻塞,无法处理任务的完成。您不应该对尚未完成的任务使用Result
属性。