Adding a synchronous caching mechanism to an asynchronous transparent method

I have a method that performs a long action using async task
Now I want to add a caching mechanism that will be transparent in the same method. Now I could always extract the result of the cache and transfer it using the Task so that it would "work", but I want to prevent the context switch that I get.

Here is an example of what I have:

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

And here is 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 am surprised to see that this compiles and works. Something tells me that I am not doing it right.

Here is another example that I 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 is the code:

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

From what I read here task.Result executes the task synchronously and returns string . Now I see the request through Fiddler, and my program gets stuck in the return task.Result line, although I see 200 OK , and I have been waiting a long time.

Bottom line:

  • What is the best \ right way to use caching inside an asynchronous method (for example, to do something synchronously in some cases without creating a context switch overhead ?
  • Why is my second block of code stuck with DownloadString ?
+5
source share
1 answer

First of all, if, after calling the async method, the returned task is already completed, there will be no context switch because there is no need. So this is perfectly 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 cases where the result is cached, the async mechanism is redundant. These overheads are mostly negligible, but you can improve performance by dropping both async and await and creating 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 "wait for someObject"; the compiler will generate code that checks whether the operation represented by some object is completed. If so, execution continues synchronously at the waiting point. If it hasnt, the generated code will connect the continuation delegate to the expected object, so that when the presented operation completes, this continuation delegate will be called

From Async / Await Frequently Asked Questions


Task.Result does not execute the task synchronously, it waits synchronously. This means that the calling thread is blocked, waiting for the task to complete. When you use this in an environment with a SynchronizationContext , which can lead to a deadlock because the thread is blocked and cannot handle the completion of the task. You should not use the Result property for a task that is not yet complete.

+5
source

Source: https://habr.com/ru/post/1205010/


All Articles