Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to conditionally run a code asynchonously using tasks

I have a class in charge of retrieving resources which also caches them for quick access. The class exposes an asynchronous method for retrieving a resource:

public Task<object> GetResourceAsync(string resourceName)
{
    return Task.Factory.StartNew<object>(() =>
    {
        // look in cache

        // if not found, get from disk

        // return resource
    });
}

The client code then looks like this:

myResourceProvider.GetResourceAsync("myResource")
    .ContinueWith<object>(t => Console.WriteLine("Got resource " + t.Result.ToString()));

This way, a background thread is always used. However, I don't want the code to run asynchronously if the object was found in the cache. If it was found in the cache, I'd like to immediately return the resource and not to have to use another thread.

Thanks.

like image 808
Adi Lester Avatar asked Dec 20 '11 13:12

Adi Lester


People also ask

How do I run a task in based on condition?

Run . Use a TaskCompletionSource which will return a finished task when a result that meets the condition is returned. If no result meets the condition then tcs. Task will never finish.

Is Task run asynchronous?

In . NET, Task. Run is used to asynchronously execute CPU-bound code.

What is the difference between task run and async await?

An await expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method. The async and await keywords don't cause additional threads to be created.


2 Answers

.NET 4.5 has Task.FromResult that lets you return a Task<T>, but instead of running a delegate on a threadpool thread, it explicitly sets the task's return value.

So in the context of your code:

public Task<object> AsyncGetResource(string resourceName)
{
    object valueFromCache;
    if (_myCache.TryGetValue(resourceName, out valueFromCache)) {
        return Task.FromResult(valueFromCache);
    }
    return Task.Factory.StartNew<object>(() =>
    {
        // get from disk
        // add to cache
        // return resource
    });
}

If you're still on .NET 4.0, you can use TaskCompletionSource<T> to do the same thing:

var tcs = new TaskCompletionSource<object>();
tcs.SetResult(...item from cache...);
return tcs.Task;
like image 199
Joe White Avatar answered Sep 21 '22 11:09

Joe White


Be careful if you have UI connected thread.

In WPF it is very important, to use the Task.Run on the UI thread (eg. a button click event handler), to avoid UI problems, and running your code on a background thread.

Why? The Task.Run by default a wrapper around the Task.Factory.StartNew with the TaskScheduler.Default parameter, instead of TaskScheduler.Current.

So not enough just to call your async method like this, because this is running on the UI thread and freezing it: await SomeTaskAsync();

Instead of it you should call it inside a Task.Run:

  • Task.Run(async() => await SomeTaskAsync());

Or use your syncron method in the Task.Run:

  • Task.Run(() => SomeTask());
like image 28
Bence Végert Avatar answered Sep 20 '22 11:09

Bence Végert