Implementing multitasking for a GUI

In this section, you will use threads to improve multitasking in a graphical app.

Note

Visual Studio Code cannot be used to create GUI applications, so you will need Visual Studio 2017 on Windows 7 or later to complete this last section of the chapter. In Chapter 13, Building Universal Windows Platform Apps Using XAML, you will learn about Universal Windows Platform apps; however, these can only be created on Windows 10. So, if you only have Visual Studio Code on a non-Windows OS, at this point, you might want to jump ahead to Chapter 14, Building Web Applications Using ASP.NET Core MVC.

C# 5 introduced two keywords to simplify working with the Task type. They are especially useful for:

  • Implementing multitasking for a graphical user interface (GUI)
  • Improving the scalability of web applications and services

In this chapter, we will explore how the async and await keywords can implement multitasking with a GUI running on Windows 7 or later.

In Chapter 14, Building Web Applications Using ASP.NET Core MVC, we will explore how the async and await keywords can improve scalability in web applications.

In Chapter 15, Building Mobile Apps Using Xamarin.Forms and ASP.NET Core Web API, we will explore how the async and await keywords can implement multitasking with a mobile GUI and improve scalability in a web service.

Creating a GUI that blocks

In Visual Studio 2017, go to File | Add | New Project.... In the Add New Project dialog, in the Installed | Templates list, select Visual C#. In the center list, select WPF App (.NET Framework), type the name as Ch12_GUITasks, and then click on OK.

You will learn more about XAML in the next chapter, but for now, just enter the following markup in the XAML view inside the <Grid> element:

    <StackPanel> 
      <Button Name="GetProductsButton">Get Products</Button> 
      <TextBox>Type in here while the products load...</TextBox> 
      <ListBox Name="ProductsListBox"></ListBox> 
    </StackPanel> 

Your main editor window should now look like the following screenshot:

Creating a GUI that blocks

Inside the Button element, after setting the Name, enter an attribute named Click, as shown in the following screenshot, and when the IntelliSense appears, press Enter to insert a new event handler:

Creating a GUI that blocks

Choose the View | Code menu, or press F7, or click on the MainWindow.xaml.cs tab.

Add the following code to the top of the code file:

    using System.Data.SqlClient; 

Add the following code inside the GetProductsButton_Click method:

    var connection = new SqlConnection( 
      @"Data Source=(localdb)mssqllocaldb;" + 
      "Initial Catalog=Northwind;Integrated Security=true;"); 
 
    connection.Open(); 
 
    var getProducts = new SqlCommand( 
      "WAITFOR DELAY '00:00:05';" + 
      "SELECT ProductID, ProductName, UnitPrice FROM Products",  
      connection); 
 
    SqlDataReader reader = getProducts.ExecuteReader(); 
 
    int indexOfID = reader.GetOrdinal("ProductID"); 
    int indexOfName = reader.GetOrdinal("ProductName"); 
    int indexOfPrice = reader.GetOrdinal("UnitPrice"); 
 
    while (reader.Read()) 
    { 
      ProductsListBox.Items.Add( 
        string.Format("{0}: {1} costs {2:c}", 
        reader.GetInt32(indexOfID),
        reader.GetString(indexOfName),
        reader.GetDecimal(indexOfPrice))); 
    } 
    reader.Dispose(); 
    connection.Dispose();

Note

The database connection string uses Microsoft SQL Server LocalDb and connects to the Northwind sample database The SQL statement waits for five seconds before returning four columns from the Products table

Run the WPF app by pressing Ctrl + F5.

Click inside the text box and enter some text. The user interface is responsive.

Click on the Get Products button and then try to enter some text in the text box again.

The user interface is blocked because the thread is busy running the SQL command, as shown in the following screenshot. Only once the list of products has been populated does the UI become responsive and you can type into the text box again.

Creating a GUI that blocks

Creating a GUI that doesn't block

The types in the SqlClient namespace have been improved in .NET Framework 4.5 and later by giving any method that might take a long time an asynchronous equivalent that returns a Task.

For example, the SqlConnection class has both an Open method that returns void and an OpenAsync method that returns Task. SqlCommand has both an ExecuteReader method that returns SqlDataReader and an ExecuteReaderAsync method that returns Task<SqlDataReader>.

We can use these Task objects as we did earlier, but that would still block the user interface when we call any of the Wait methods.

Instead, we can use the await keyword for any Task. This means that the main thread will not be blocked while we wait, but will remember its current position within the statements so that, once the Task has completed, the main thread continues executing from that same point. This allows us to write code that looks as simple as synchronous, but underneath, it is much more complex.

Note

Internally, await creates a state machine to manage the complexity of passing state between any worker threads and the user interface thread.

Modify the statements as shown in the following code. Note that, to use the await keyword, we must mark the containing method await with the async keyword. They always work as a pair:

    private async void GetProductsButton_Click( 
      object sender, RoutedEventArgs e) 
    { 
      var connection = new SqlConnection( 
        @"Data Source=(localdb)mssqllocaldb;" + 
        "Initial Catalog=Northwind;Integrated Security=true;"); 
      await connection.OpenAsync(); 
      var getProducts = new SqlCommand( 
        "WAITFOR DELAY '00:00:05';" + 
        "SELECT ProductID, ProductName, UnitPrice FROM Products",  
        connection); 
      SqlDataReader reader = await getProducts.ExecuteReaderAsync(); 
      int indexOfID = reader.GetOrdinal("ProductID"); 
      int indexOfName = reader.GetOrdinal("ProductName"); 
      int indexOfPrice = reader.GetOrdinal("UnitPrice"); 
      while (await reader.ReadAsync()) 
      { 
        ProductsListBox.Items.Add( 
          string.Format("{0}: {1} costs {2:c}", 
          await reader.GetFieldValueAsync<int>(indexOfID), 
          await reader.GetFieldValueAsync<string>(indexOfName), 
          await reader.GetFieldValueAsync<decimal>(indexOfPrice))); 
      } 
      reader.Dispose(); 
      connection.Dispose(); 
    } 

Rerun the application by pressing Ctrl + F5.

This time, after clicking on the Get Products button, you will be able to enter text in the text box while the command executes:

Creating a GUI that doesn't block

Other types with Async methods

Here are some of the other types that have an asynchronous method support:

Type

Methods

HttpClient

GetAsync, PostAsync, PutAsync, DeleteAsync, SendAsync

StreamReader

ReadAsync, ReadLineAsync, ReadToEndAsync

StreamWriter

WriteAsync, WriteLineAsync, FlushAsync

Tip

Good Practice

Any time you see a method that ends in the suffix Async, check to see whether it returns Task or Task<T>. If it does, then you should use it instead of the synchronous non-Async suffixed method. Remember to call it using await and decorate your method with async.

await in catch blocks

In C# 5, it was only possible to use the await keyword in a try exception handling block, but not in a catch block.

In C# 6 or later, it is now possible to use await in both try and catch blocks.

Improving scalability for client-server applications

In the previous example, we saw how using the async and await keywords can improve the performance of a client-side graphical application by preventing the blocking of the user interface thread.

The same keywords can be applied on the server side when building web applications and services. From the client application's point of view, nothing changes (or they might even notice a small increase in the time for a request to return). So, from a single client's point of view, the use of async on the server side makes their experience worse!

On the server side, additional, cheaper worker threads are created to wait for long-running tasks to finish so that expensive IO threads can handle other client's requests instead of being blocked. This improves the overall scalability of a web application or service. More clients can be supported simultaneously.

Note

You will create asynchronous operations on the server side in Chapter 14, Building Web Applications Using ASP.NET Core MVC.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.62.197