Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a synchronous caching mechanism to an async method "transparently"

I have a method which does a long action using an async task
Now, I want to add a cache mechanism that will be transparent in the same method. Now, I could always fetch my cache result and wrap it with a Task so it will "work" but I want to prevent the context switch that I will get.

Here's an example of what I have:

var result = await LongTask();

private async Task<string> LongTask()
{
   return await DoSomethingLong();
}

And here's an example of what I want:

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();
}

Now I'm surprised to see that this compiled and worked
Something tells me that I'm not doing it correctly.

Here's another similar example that I've tested:

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;
    }
}

And here's the code:

var res = DownloadString(true);
string str1 = await res;
var res2 = DownloadString(false);
string str2 = await res2;

From what I've read here task.Result executes the task synchronously and returns a string. Now I see the request via Fiddler and my program get's stuck on the return task.Result line even though I see a 200 OK and I wait a long time.

Bottom Line:

  1. Whats the best\correct way to use caching inside an async method(e.g. doing something synchronously in some cases without create a context switch overhead?
  2. Why does my second block of code with the DownloadString get stuck?
like image 315
Amir Popovich Avatar asked Oct 19 '14 12:10

Amir Popovich


People also ask

What is asynchronous caching?

Asynchronous caching simplifies the role of the application client in data caching and can significantly improve the performance of certain workloads. This post explains data caching behavior in Alluxio v1.

When should you not use cache?

Three caching challenges to consider Caches take up space on the disk, so we have to assess whether the time we are saving is worth the amount of disk space used. Cached data might not be the most accurate, particularly for volatile real-time data. Therefore, volatile data should not be cached.

How do you create asynchronous method?

The first step is to add the async keyword to the method. It appears in the method signature in the same way that the static keyword does. Then, we need to wait for the download using the await keyword. In terms of C# syntax, await acts as a unary operator, like the !

What is async return method?

Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.

What types of data do you think are most important to have cached for quick distribution?

In-memory data lookup: If you have a mobile / web app front end you might want to cache some information like user profile, some historical / static data, or some api response according to your use cases. Caching will help in storing such data.

What is caching and how it works?

In computing, a cache is a high-speed data storage layer which stores a subset of data, typically transient in nature, so that future requests for that data are served up faster than is possible by accessing the data's primary storage location.


1 Answers

First of all, if after a call to an async method the returned task is already completed there would be no context switch, because none is needed. So this is completely acceptable:

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();
}

However, in the cases where the result is cached, the async mechanism is redundant. This overhead is mostly negligible but you can improve performance by dropping both the async and await and create a completed task using 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

From Async/Await FAQ


Task.Result doesn't execute the task synchronously, it waits synchronously. That means that the calling thread is blocked waiting for the task to complete. When you use that in an environment with a SynchronizationContext that may lead to a deadlock since the thread is blocked and can't handle the task's completion. You shouldn't use the Result property on a task that hasn't completed yet.

like image 63
i3arnon Avatar answered Oct 22 '22 01:10

i3arnon