c# - 为异步方法添加同步缓存机制 "transparently"

标签 c# .net asynchronous task-parallel-library async-await

我有一个使用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 并且我等待了很长时间。

底线:

  1. 在异步方法中使用缓存的最佳\正确方法是什么(例如,在某些情况下同步执行某些操作而不会产生上下文切换开销
  2. 为什么我的带有 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机制是多余的。这种开销几乎可以忽略不计,但您可以通过删除 asyncawait 并使用 Task.FromResult 创建完成的任务来提高性能:

private Task<string> LongTask()
{
   if(isInCache)
   { 
      return Task.FromResult(cachedValue()); 
   }

   // else do the long thing and return my Task<string>
   return DoSomethingLong();
}

...when you write “await someObject;” the compiler will generate code that checks whether the operation represented by someObject has already completed. If it has, execution continues synchronously over the await point. If it hasn’t, the generated code will hook up a continuation delegate to the awaited object such that when the represented operation completes, that continuation delegate will be invoked

来自 Async/Await FAQ


Task.Result 不会同步执行任务,它会同步等待。这意味着调用线程被阻塞等待任务完成。当您在具有 SynchronizationContext 的环境中使用它时,由于线程被阻塞并且无法处理任务的完成,可能会导致死锁。您不应在尚未完成的任务上使用 Result 属性。

关于c# - 为异步方法添加同步缓存机制 "transparently",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26450645/

相关文章:

c# - C# 中的间隔容器

c# - 变量在循环中保留其值?

c# - .Net Core Web API 方法是否与 EF Core 查询异步

c# - 在表达式树中引用 "this"

c# - 像下面这样的简单构造

C# 简化字符串数组初始化

c# - 在 Task.Run 中使用 CancellationToken 超时不起作用

c# - Visual Studio 2015 表示 'cast is redundant' 。为什么?

javascript - 即使从 MDN 复制,Await 也是保留字

javascript - 执行componentDidMount后无法设置状态