Asked  7 Months ago    Answers:  5   Viewed   272 times

I would like to ask you on your opinion about the correct architecture when to use Task.Run. I am experiencing laggy UI in our WPF .NET 4.5 application (with Caliburn Micro framework).

Basically I am doing (very simplified code snippets):

public class PageViewModel : IHandle<SomeMessage>
{
   ...

   public async void Handle(SomeMessage message)
   {
      ShowLoadingAnimation();

      // Makes UI very laggy, but still not dead
      await this.contentLoader.LoadContentAsync();

      HideLoadingAnimation();
   }
}

public class ContentLoader
{
    public async Task LoadContentAsync()
    {
        await DoCpuBoundWorkAsync();
        await DoIoBoundWorkAsync();
        await DoCpuBoundWorkAsync();

        // I am not really sure what all I can consider as CPU bound as slowing down the UI
        await DoSomeOtherWorkAsync();
    }
}

From the articles/videos I read/saw, I know that await async is not necessarily running on a background thread and to start work in the background you need to wrap it with await Task.Run(async () => ... ). Using async await does not block the UI, but still it is running on the UI thread, so it is making it laggy.

Where is the best place to put Task.Run?

Should I just

  1. Wrap the outer call because this is less threading work for .NET

  2. , or should I wrap only CPU-bound methods internally running with Task.Run as this makes it reusable for other places? I am not sure here if starting work on background threads deep in core is a good idea.

Ad (1), the first solution would be like this:

public async void Handle(SomeMessage message)
{
    ShowLoadingAnimation();
    await Task.Run(async () => await this.contentLoader.LoadContentAsync());
    HideLoadingAnimation();
}

// Other methods do not use Task.Run as everything regardless
// if I/O or CPU bound would now run in the background.

Ad (2), the second solution would be like this:

public async Task DoCpuBoundWorkAsync()
{
    await Task.Run(() => {
        // Do lot of work here
    });
}

public async Task DoSomeOtherWorkAsync(
{
    // I am not sure how to handle this methods -
    // probably need to test one by one, if it is slowing down UI
}

 Answers

22

Note the guidelines for performing work on a UI thread, collected on my blog:

  • Don't block the UI thread for more than 50ms at a time.
  • You can schedule ~100 continuations on the UI thread per second; 1000 is too much.

There are two techniques you should use:

1) Use ConfigureAwait(false) when you can.

E.g., await MyAsync().ConfigureAwait(false); instead of await MyAsync();.

ConfigureAwait(false) tells the await that you do not need to resume on the current context (in this case, "on the current context" means "on the UI thread"). However, for the rest of that async method (after the ConfigureAwait), you cannot do anything that assumes you're in the current context (e.g., update UI elements).

For more information, see my MSDN article Best Practices in Asynchronous Programming.

2) Use Task.Run to call CPU-bound methods.

You should use Task.Run, but not within any code you want to be reusable (i.e., library code). So you use Task.Run to call the method, not as part of the implementation of the method.

So purely CPU-bound work would look like this:

// Documentation: This method is CPU-bound.
void DoWork();

Which you would call using Task.Run:

await Task.Run(() => DoWork());

Methods that are a mixture of CPU-bound and I/O-bound should have an Async signature with documentation pointing out their CPU-bound nature:

// Documentation: This method is CPU-bound.
Task DoWorkAsync();

Which you would also call using Task.Run (since it is partially CPU-bound):

await Task.Run(() => DoWorkAsync());
Tuesday, June 1, 2021
 
tplaner
answered 7 Months ago
56

Always use REST. It is the most modern, advanced and scalable integration approach available today. Load balancing a REST based service is achieved simply with hardware or software HTTP load balancer and can be considered just as free as load balancing in JMS.

MOM (Message Oriented Middleware) doesn't scale easily (but may scale big enough for your needs). REST works at web scale.

MOM does not have economies of scale. For data retrieval requests, each time a particular piece of data is requested, another message must be sent to the server and responded to by the server. In a REST based system, requests for the same data can be serviced by a HTTP cache. This means that as the volume of requests increase over time, a MOM based system will see the server load increase at the same rate as the requests. A REST based system will see the the server load increase at a slower rate than the requests.

MOM will tempt you with fire-and-forget messages with guaranteed delivery, only to bite you with the chain of custody problem.

MOM is terrible for synchronous request-reply as it will fail slowly (i.e. wait for timeout) when the server is down. When a request is going to fail, you want it to fail fast. A HTTP request to a REST based service will fail immediately (on the TCP connect) if the server is down.

MOM is useful for asynchronous request-reply messaging, but then you'll be left with the problem of where to store the state in-between the request and the reply (Hint: Your options are File or Regular Database, the Message or a NoSQL Database). Often the extra implementation effort is not worth the perceived advantages of asynchronicity. Also REST based services do support asynchronous requests if you really need it. 202 Accepted is your friend in this situation.

Finally, the use of caching allows REST based systems to implement pull-based integrations, which are far easier to support. For instance, just say we want to move data from system A to system B. The MOM approach would be to send messages from A to B. A REST based approach would be to create a data feed service in A (like an RSS feed) that B polls for new data (the same way your RSS reader polls for new articles). When B fails, in the MOM example, the support team will need to monitor the message queues to make sure they don't overflow, while someone else get's B back up. In the REST example, the support team just has to worry about getting B back up. There isn't much of a difference when A fails. In the MOM example B doesn't know and doesn't care. In the REST example B does know that A is down, but it still doesn't care because obviously there is no new data from A when it's down. Initially the polling that pull-based integration requires seams very inefficient, however HTTP caching makes this a non-issue.

In other words, instead of investing in a JMS server, invest in a good caching HTTP load balancer.

Sunday, August 8, 2021
 
Sergey Ryabov
answered 4 Months ago
44

I solved it and posting it here, might help anyone having the same issue.

My initial need was a small helper that would quickly download images but also just drop the connection if server does not respond quickly, all this in parallel and asynchronously.

This helper will return you a tuple that contains the remote path, the local path and the exception if one occurred; so quite useful as it's always good to know why faulty downloads have faulted. I think I forgot none of the situations that can occur for a download but you're welcome to comment it.

  • You specify a list of urls to download
  • You can specify a local file name where it will be saved, if not one will be generated for you
  • Optionally a duration for cancelling a download (handy for slow or unreachable servers)

You can just use DownloadFileTaskAsync itself or use the ForEachAsync helper for parallel and asynchronous downloads.

Code with an example on how to use it :

private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    IEnumerable<string> enumerable = your urls here;
    var results = new List<Tuple<string, string, Exception>>();
    await enumerable.ForEachAsync(s => DownloadFileTaskAsync(s, null, 1000), (url, t) => results.Add(t));
}

/// <summary>
///     Downloads a file from a specified Internet address.
/// </summary>
/// <param name="remotePath">Internet address of the file to download.</param>
/// <param name="localPath">
///     Local file name where to store the content of the download, if null a temporary file name will
///     be generated.
/// </param>
/// <param name="timeOut">Duration in miliseconds before cancelling the  operation.</param>
/// <returns>A tuple containing the remote path, the local path and an exception if one occurred.</returns>
private static async Task<Tuple<string, string, Exception>> DownloadFileTaskAsync(string remotePath,
    string localPath = null, int timeOut = 3000)
{
    try
    {
        if (remotePath == null)
        {
            Debug.WriteLine("DownloadFileTaskAsync (null remote path): skipping");
            throw new ArgumentNullException("remotePath");
        }

        if (localPath == null)
        {
            Debug.WriteLine(
                string.Format(
                    "DownloadFileTaskAsync (null local path): generating a temporary file name for {0}",
                    remotePath));
            localPath = Path.GetTempFileName();
        }

        using (var client = new WebClient())
        {
            TimerCallback timerCallback = c =>
            {
                var webClient = (WebClient) c;
                if (!webClient.IsBusy) return;
                webClient.CancelAsync();
                Debug.WriteLine(string.Format("DownloadFileTaskAsync (time out due): {0}", remotePath));
            };
            using (var timer = new Timer(timerCallback, client, timeOut, Timeout.Infinite))
            {
                await client.DownloadFileTaskAsync(remotePath, localPath);
            }
            Debug.WriteLine(string.Format("DownloadFileTaskAsync (downloaded): {0}", remotePath));
            return new Tuple<string, string, Exception>(remotePath, localPath, null);
        }
    }
    catch (Exception ex)
    {
        return new Tuple<string, string, Exception>(remotePath, null, ex);
    }
}

public static class Extensions
{
    public static Task ForEachAsync<TSource, TResult>(
        this IEnumerable<TSource> source,
        Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor)
    {
        var oneAtATime = new SemaphoreSlim(5, 10);
        return Task.WhenAll(
            from item in source
            select ProcessAsync(item, taskSelector, resultProcessor, oneAtATime));
    }

    private static async Task ProcessAsync<TSource, TResult>(
        TSource item,
        Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor,
        SemaphoreSlim oneAtATime)
    {
        TResult result = await taskSelector(item);
        await oneAtATime.WaitAsync();
        try
        {
            resultProcessor(item, result);
        }
        finally
        {
            oneAtATime.Release();
        }
    }
}

I haven't changed the signature of ForEachAsync to choose the level of parallelism, I'll let you adjust it as you wish.

Output example :

DownloadFileTaskAsync (null local path): generating a temporary file name for http://cache.thephoenix.com/secure/uploadedImages/The_Phoenix/Music/CD_Review/main_OTR_Britney480.jpg
DownloadFileTaskAsync (null local path): generating a temporary file name for http://ssimg.soundspike.com/artists/britneyspears_femmefatale_cd.jpg
DownloadFileTaskAsync (null local path): generating a temporary file name for http://a323.yahoofs.com/ymg/albumreviewsuk__1/albumreviewsuk-526650850-1301400550.jpg?ymm_1xEDE5bu0tMi
DownloadFileTaskAsync (null remote path): skipping
DownloadFileTaskAsync (time out due): http://hangout.altsounds.com/geek/gars/images/3/9/8/5/2375.jpg
DownloadFileTaskAsync (time out due): http://www.beat.com.au/sites/default/files/imagecache/630_315sr/images/article/header/2011/april/britney-spears-femme-fatale.jpg
DownloadFileTaskAsync (time out due): http://cache.thephoenix.com/secure/uploadedImages/The_Phoenix/Music/CD_Review/main_OTR_Britney480.jpg
DownloadFileTaskAsync (downloaded): http://newblog.thecmuwebsite.com/wp-content/uploads/2009/12/britneyspears1.jpg
DownloadFileTaskAsync (downloaded): http://newblog.thecmuwebsite.com/wp-content/uploads/2009/12/britneyspears1.jpg
DownloadFileTaskAsync (downloaded): http://static.guim.co.uk/sys-images/Music/Pix/site_furniture/2011/3/22/1300816812640/Femme-Fatale.jpg
DownloadFileTaskAsync (downloaded): http://www.sputnikmusic.com/images/albums/72328.jpg

What used to take up to 1 minute now barely takes 10 seconds for the same result :)

And big thanks to the author of these 2 posts :

http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx

http://blogs.msdn.com/b/pfxteam/archive/2012/03/04/10277325.aspx

Monday, August 9, 2021
 
Denis
answered 4 Months ago
45

When to use Task.Run, when to use async- await and when to use them in combination

I have an entire blog series on the subject, but in summary:

  • Use Task.Run to move CPU-bound (or blocking) work off of the UI thread for apps with a UI.
  • Use async/await for I/O-bound work.

There are some more rare cases where Task.Run or async can be useful, but the two points above provide guidance for most situations.

Does Task.Run means background thread will behave as a blocking thread?

Task.Run can actually run synchronously or asynchronously, but it does use thread pool threads.

i am trying to create a desktop console application in .net 4.5

Trying to download multiple large images from internet. How should i use these keywords in combination and why?

This is an I/O-bound operation in a non-UI app, so you should definitely prefer async/await.

Saturday, August 28, 2021
 
Rakesh
answered 3 Months ago
40

Getting Stephen Cleary's IAsyncCommand pattern working with functions that take a parameter when producing the Task to be executed would require just a few tweaks to his AsyncCommand class and static helper methods.

Starting with his classes found in the AsyncCommand4 sample in the link above, let's modify the constructor to take a function with inputs for a parameter (of type object - this will be the Command Parameter) as well as a CancellationToken and returning a Task. We will also need to make a single change in the ExecuteAsync method so we can pass the parameter into this function when executing the command. I created a class called AsyncCommandEx (shown below) that demonstrates these changes.

public class AsyncCommandEx<TResult> : AsyncCommandBase, INotifyPropertyChanged
{
    private readonly CancelAsyncCommand _cancelCommand;
    private readonly Func<object, CancellationToken, Task<TResult>> _command;
    private NotifyTaskCompletion<TResult> _execution;

    public AsyncCommandEx(Func<object, CancellationToken, Task<TResult>> command)
    {
        _command = command;
        _cancelCommand = new CancelAsyncCommand();
    }

    public ICommand CancelCommand
    {
        get { return _cancelCommand; }
    }

    public NotifyTaskCompletion<TResult> Execution
    {
        get { return _execution; }
        private set
        {
            _execution = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public override bool CanExecute(object parameter)
    {
        return (Execution == null || Execution.IsCompleted);
    }

    public override async Task ExecuteAsync(object parameter)
    {
        _cancelCommand.NotifyCommandStarting();
        Execution = new NotifyTaskCompletion<TResult>(_command(parameter, _cancelCommand.Token));
        RaiseCanExecuteChanged();
        await Execution.TaskCompletion;
        _cancelCommand.NotifyCommandFinished();
        RaiseCanExecuteChanged();
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    private sealed class CancelAsyncCommand : ICommand
    {
        private bool _commandExecuting;
        private CancellationTokenSource _cts = new CancellationTokenSource();

        public CancellationToken Token
        {
            get { return _cts.Token; }
        }

        bool ICommand.CanExecute(object parameter)
        {
            return _commandExecuting && !_cts.IsCancellationRequested;
        }

        void ICommand.Execute(object parameter)
        {
            _cts.Cancel();
            RaiseCanExecuteChanged();
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void NotifyCommandStarting()
        {
            _commandExecuting = true;
            if (!_cts.IsCancellationRequested)
                return;
            _cts = new CancellationTokenSource();
            RaiseCanExecuteChanged();
        }

        public void NotifyCommandFinished()
        {
            _commandExecuting = false;
            RaiseCanExecuteChanged();
        }

        private void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }
}

It will also be helpful to update the static AsyncCommand helper class to make the the creation of Command Parameter-aware IAsyncCommands easier. To handle the possible combinations of functions that do or do not take a Command Parameter we will double the number of methods but the result is not too bad:

public static class AsyncCommandEx
{
    public static AsyncCommandEx<object> Create(Func<Task> command)
    {
        return new AsyncCommandEx<object>(async (param,_) =>
                                              {
                                                  await command();
                                                  return null;
                                              });
    }

    public static AsyncCommandEx<object> Create(Func<object, Task> command)
    {
        return new AsyncCommandEx<object>(async (param, _) =>
        {
            await command(param);
            return null;
        });
    }

    public static AsyncCommandEx<TResult> Create<TResult>(Func<Task<TResult>> command)
    {
        return new AsyncCommandEx<TResult>((param,_) => command());
    }

    public static AsyncCommandEx<TResult> Create<TResult>(Func<object, Task<TResult>> command)
    {
        return new AsyncCommandEx<TResult>((param, _) => command(param));
    }

    public static AsyncCommandEx<object> Create(Func<CancellationToken, Task> command)
    {
        return new AsyncCommandEx<object>(async (param, token) =>
                                              {
                                                  await command(token);
                                                  return null;
                                              });
    }

    public static AsyncCommandEx<object> Create(Func<object, CancellationToken, Task> command)
    {
        return new AsyncCommandEx<object>(async (param, token) =>
        {
            await command(param, token);
            return null;
        });
    }

    public static AsyncCommandEx<TResult> Create<TResult>(Func<CancellationToken, Task<TResult>> command)
    {
        return new AsyncCommandEx<TResult>(async (param, token) => await command(token));
    }

    public static AsyncCommandEx<TResult> Create<TResult>(Func<object, CancellationToken, Task<TResult>> command)
    {
        return new AsyncCommandEx<TResult>(async (param, token) => await command(param, token));
    }
}

To continue with Stephen Cleary's sample, you can now build an AsyncCommand that takes an object parameter passed in from the Command Parameter (which can be bound to the UI):

CountUrlBytesCommand = AsyncCommandEx.Create((url,token) => MyService.DownloadAndCountBytesAsync(url as string, token));
Thursday, October 28, 2021
 
spassen
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share