将同步缓存机制添加到异步方法"透明地"

时间:2014-10-19 12:42:27

标签: 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. 什么是在异步方法中使用缓存的最佳\正确方法(例如,在某些情况下同步执行某些操作而不创建context switch overhead
  2. 为什么我的第二段代码DownloadString被卡住了?

1 个答案:

答案 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机制是多余的。此开销几乎可以忽略不计,但您可以通过删除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();
}
  

...当您编写“await someObject;”时,编译器将生成检查someObject表示的操作是否已完成的代码。如果有,则执行在等待点上同步继续。如果没有,生成的代码将连续委托连接到等待的对象,这样当表示的操作完成时,将调用该连续委托

来自Async/Await FAQ


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