How to Optimize a Method with Async and Parallel.ForEach

Objective: Learn to improve the execution time of a method using Async and Parallel.ForEach

You need to know: C#, basic understanding of how Async/Await works, and the basics of Xamarin.

Let’s begin:

For this tutorial first we will create a synchronous method that returns a list of strings containing websites’ data.

Then we will optimize it with Async and Parallel.ForEach

Create a new project and replace the MainPage.xaml code with this one:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:TTXAsyncParallel" x:Class="TTXAsyncParallel.MainPage">
    <StackLayout>
        <Button Text="Start Synchronous Method" Clicked="BtnStart"/>
        <Label x:Name="lblTime" Text=""/>
        <ListView x:Name="lstResults"/>
    </StackLayout>
</ContentPage>

 

And the MainPage.xaml.cs code with this one:

using Xamarin.Forms;

namespace TTXAsyncParallel
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void BtnStart(object sender, System.EventArgs e)
        {

        }
    }
}

Read the code from both files and put the name of your project instead of “TTXAsyncParallel”.

Run it and you should see something like this:

 

Next, add a class called Methods and add the following methods:

public static List<string> WebsitesUrls()
        {
            var output = new List<string>
            {
                "https://news.ycombinator.com/",
                "https://microsoft.com",
                "https://amazon.com",
                "https://twitter.com",
                "https://trello.com",
                "https://dropbox.com",
                "https://mozilla.org",
                "https://github.com",
                "https://spotify.com",
                "https://en-us.sennheiser.com"
            };

            return output;
        }

This is a simple method that will return a List with 10 websites’ URLS.

To use the Generic List remember to add the following Using Directive:


using System.Collections.Generic;

 

Now, add:

public static string GetStr(string url)
        {
            var webClient = new WebClient();
            var result = webClient.DownloadString(url);
            return $"Site length: {result.Length} characters.";
        }

1- To use the WebClient you need to add the following Using Directive: using System.Net;

2- We use the WebClient object to get the html from the provided URL and store it in the “result” variable.

3- Since the complete html from a website is very large we make this method return only it’s character length.

 

And finally, add:

public static List<string> GetData()
        {
            var output = new List<string>();
            var websites = WebsitesUrls();

            foreach (var site in websites)
            {
                output.Add(GetStr(site));
            }

            return output;
        }

1- This will be the method that we are going to optimize. It will return a List of strings with websites’ data.

2- The websites URL’s that this method will use will come from the “WebsitesUrls” method.

3- We use a foreach to call the “GetStr” method for each site we got from the “WebsitesUrls” method and add the resulting string to the List of strings this method will return.

 

Go back to MainPage.xaml.cs and add this to the BtnStart method:

private void BtnStart(object sender, System.EventArgs e)
        {
            var time = System.Diagnostics.Stopwatch.StartNew();

            lstResults.ItemsSource = Methods.GetData();

            time.Stop();
            lblTime.Text = $"Time: { time.ElapsedMilliseconds}ms";
        }

1- We create a stopwatch to compare how fast this method will be against the optimized one.

2- We call the unoptimized method and assign the returned list to the ListView.

3- Once finished we display the elapsed time.

 

Run the app and you should see something like this. The first time you call the method will take more time (click to play video):

 

Take note of the time. Now it’s time to optimize the method.

Go to the Methods class and add the following method:

public static async Task<List<string>> GetDataAsyncParallel()
        {
            var output = new List<string>();
            var websites = WebsitesUrls();

            await Task.Run(() =>
            {
                Parallel.ForEach(websites, (site) =>
                {
                    var results = GetStr(site);
                    output.Add(results);
                });
            });

            return output;
        }

1- This is the optimized version of the “GetData” method.

Since this method will return a Task object (with a List of strings) we start by making it async.

And to use the Task object we need to add the following Using Directive:

using System.Threading.Tasks;

2- Same as the unoptimized method, the websites URL’s that this method will use will come from the “WebsitesUrls” method.

3- Task.Run says run the stuff inside the parenthesis asynchronously. It takes a lambda expression.

4- Here we use Parallel.ForEach from System.Threading.Tasks. It’s basically a Foreach that uses available threads to run it in parallel and achieve a faster execution time.

This Parallel Foreach method takes our list of strings(the websites’ URL’s) as the first parameter and as second parameter it takes an Action. So we use a lambda expression to call the “GetStr” method for each site in our list.

5- The results from the “GetStr” method are added to the list that the method will return.

 

Go back to MainPage.xaml and add this button:

<Button Text="Start Async + Parallel Method" Clicked="BtnStartAsyncParallel"/>

And now go to MainPage.xaml.cs and add the following method:

private async void BtnStartAsyncParallel(object sender, System.EventArgs e)
        {
            var time = System.Diagnostics.Stopwatch.StartNew();

            lstResults.ItemsSource = await Methods.GetDataAsyncParallel();

            time.Stop();
            lblTime.Text = $"Time: { time.ElapsedMilliseconds}ms";
        }

1- We create a stopwatch to compare how fast this method will be against the unoptimized one.

2- We call the optimized method and assign the returned list to the ListView.

3- Once finished we display the elapsed time.

 

Run the app and you should see how the Async + Parallel method is faster (click to play video):

 

About the author

AezioX

Programmer


>