Chapter 15

Asynchronous programming with X#

Asynchronous programming means calling a method (or function) asynchronously. These methods are called asynchronous methods.

When an asynchronous method is called, it returns immediately but it will deliver its return value at a later time.

This means that the caller does not have to wait for the asynchronous method to complete.

The main benefit of calling a method asynchronously is that it does not block the program. This is especially important within a WinForm or WPF application that runs on a single thread.

For Web Applications that have to handle many requests at the same time, it's a requirement for scalability.

Running a method asynchronously was possible from version 1.0 of the .Net Framework. With version 4.0 it became much easier thanks to the keywords async and await.

This chapter is about asynchronous programming with async and await in X#.

For "historical reasons" and because the techniques are still useful, this chapter starts with a short overview of how asynchronous methods were implemented in the early days of the .Net Framework.


NOTE: First, a personal note. While writing this chapter and programming the small examples, I had some doubts. First, I am not a real expert on this topic. I have used the async/await keywords several times in the last few years, but I am still not sure if I really understand all the concepts. While with other topics like database access or using the file system, it is OK to learn while you are implementing a specific requirement, I would not recommend this approach for the async/await pattern because it more to know than just new keywords and the Task class with its methods.


There is a really good tutorial by Raw Coding on YouTube which I can recommend for getting a better understanding (the author shows how the compiler makes all the "magic" possible by implementing a state machine, with > 700 comments the video also draws a lot of discussions). Second, whenever I used async/await I was not sure if it was really necessary or if a simple BackgroundWorkerClass would have been an easier alternative. But besides my doubts, the "new" way of doing asynchronous calls is a powerful technique that every X# developer should know about.

A very short history of asynchronous method calls in the .Net Framework

The possibility for asynchronous method calls was part of the .Net Framework from version 1.0. It was called the Asynchronous Model Pattern or AMP for short. The key ingredients are the BeginInvoke() and EndInvoke() methods of a Delegate and therefore of a standard callback mechanism. Thanks to this pattern, any method could be called asynchronously but with extra effort due to the need to either poll for results or use the callback.

With .Net 2.0, many classes in the .Net Runtime got "Begin-methods" like BeginRead() of System.IO.Stream or BeginExecuteReader() of System.Data.SqlClient.SqlCommand for doing standard operations the AMP way.


NOTE: There was another pattern in the past that is called Event Asynchronous Pattern (EAP). I have never used it. There is a good overview if you follow this link: https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/


A big improvement came with .Net Framework 4.0 with the integration of the Task Parallel Library (TPL) into the namespace System.Threading.Tasks of the base class library. The result was not only new classes like the task class, two new language keywords async and await, but also many methods like ReadAsync of System.IO.FileStream or ExecuteReaderAsync of System.Data.DbCommand and other classes that can be "awaited" with await so that they are run asynchronously. The new pattern is called TAP for Task Asynchronous Programming (probably one the last acronyms in the Microsoft world that had not been used for something else;).


NOTE: An official pattern is more than an interesting side note, it's also a kind of obligation for all developers using that pattern. One of the obligations is to follow the naming conventions for methods that should always end with the word "Async".


In this chapter, I will show an example of the old pattern for "historical reasons" only. Although the Begin methods can still be used and they might be easier to understand for developers who have no experience with asynchronous calls at all, I really recommend using the "new" async methods because they are an integral part of the .Net runtime with language support in all the .Net languages including X#.

You will find async and await in other languages too, for example in JavaScript. So, it's more a universal pattern than a language-specific extension.

Doing asynchronous method calls the old way

In the good old days, calling a method asynchronously required a delegate (as a typesafe callback):

Delegate AsyncMethodCaller(i As Int) As Int

This delegate (as usually, the name does not matter) can be used for any method with a int argument and return value of int:

// Long running method that should be better called asynchronously

Static Method DoSomething(Count As Int) As Int

Now comes the BeginInvoke() call:

// Call DoSomething() asynchronously and get an IAsyncResult object back var result := caller:BeginInvoke(3,null,null)

It's important to understand that BeginInvoke() does not return the return value of the method, instead it always returns an IAsyncResult object. To get the real return value, EndInvoke() is required:

// Get the result back

var returnValue := caller.EndInvoke(result)

Example 15.1: (XS_BeginInvoke.prg) ** You will find the source code in the repository

There is more to this of course.

There are Begin methods in the .Net runtime for asynchronous file I/O and database access. And is possible to call BeginInvoke() with a callback delegate and to use polling to wait for an asynchronous Begin method to complete. But I won't discuss these techniques for two reasons:

  1. I don't think anybody is using them anymore because of the much better alternative with async and await and Task objects

  2. There are so many important techniques to learn, don't waste your time with techniques that have been considered legacy for many years

And now: async and wait

The .Net Framework was never intended for mobile applications that run on smartphones. When the .Net Framework came out in 2002, the Nokia 7650 was state of the art with 4 MB memory, a 176 x 208 display, and a 0.3-megapixel camera (which for me would be still sufficient today;). As we all know, the first iPhone appeared five years later in 2007. When Microsoft introduced the Windows Runtime (WinRT) as a potential successor to the .Net Framework in 2012, every method was designed for asynchronous calls from the beginning. Times were changing.

To address the changing needs of many developers, .Net 4.0 contained the Task Parallel Library (TPL) that brought three important additions:

  1. Nearly a dozen classes in the new namespace System.Threading.Tasks with the Task class in the center
  2. Two new language keywords async and await for all the .Net languages
  3. Async methods for all the important base classes in the .Net runtime

Since async and await are also part of X#, all the asynchronous programming examples you will find for C# in the Microsoft documentation can be applied 1:1 for X# (which is very good).

Keyword Bedeutung
async Declares a method (or function) as asynchronous. But it does only make sense when there is a Task object involved that can be awaited.
await Awaits an asynchronous method so that the next operation will not wait until the method is completed. Instead, the execution of the calling method continues.

Asynchronous operations are one of these topics that take (at least me) several months to get used to.

But it's worth it.

The Microsoft documentation explains the topic very well with many examples. I recommend reading the first pages and trying out the examples before you start implementing asynchronous calls in your application.

The challenge is not so much the syntax but more advanced topics like what is the right return value for an asynchronous method or how to synchronize several tasks. The biggest challenge for me still is the question about how several asynchronous calls can be "chained", so that step 2 can rely on the data that was created in step 1.

It's important to understand that you don't have to understand the inner workings of asynchronous calls to use them. The .Net runtime contains many classes that contain async methods that are simple to use because they don't differ from their synchronous counterpart.


NOTE: All the examples in this chapter do not use Function Start() as an entry point because it cannot be declared with async but instead a class App with a static Start() method. Therefore, each prg file has to be compiled with the /main:App switch.


A first example of an asynchronous method using async and await

Asynchronous programming is not difficult, but it's not easy either (at least not for me). Most developers are so used to a strictly synchronous execution path inside a program, that it can be a real challenge to "rewire" your brain to the fact that a method call does not immediately return a value but at a later time while the calling method continues. Maybe it's a question of age too.

The following example is a typical "Hello, World" example that does not do so much except use the new syntax and having a different execution in comparison to a synchronous program.

Let a look at a simple method first that does something take might take a little longer because it searches a directory tree for certain files:

// Something that would take a little longer in a real application
Static Async Method GetFilesAsync(Path As String) As Task<List<FileInfo>>
  // Create a new task and await it so it returns List<FileInfo>
  var files := await Task.Run({ => 
      var dir := DirectoryInfo{Path}
      // There is no built-in GetFilesAsync() method
     var dirFiles := dir:GetFiles("*",SearchOption.AllDirectories):Where({ f => f:Extension == ".png" .or. f:Extension == ".jpg"}):ToList()
     return dirFiles
    })
    //The rest of the method is run when the task has been completed
    // other commands
  return files 

This method is different for three reasons:

  1. It starts with the Async keyword
  2. Then return data type is Task> although it only returns a List
  3. Its awaits the Run() method of the Task class that runs an anonymous expression asynchronously

Of course, it's not enough to declare a method with the async keyword (otherwise I could write an "Async project converter" that just adds an async to every method and I would earn a lot of money and fame). The method has to use a Task object for the operation.

It's a little bit like doing your business laundry. Imagine you have to clean three shirts and two pants. If you clean them one by one it takes probably the whole day and you can't do anything else. This is not the way it's done. If you can afford it, you would give all the items to a dry cleaner. You will get a ticket back immediately. Instead of waiting for the laundry you can do something else (and if you stay in a hotel, the laundry might be hanging in the closet the next day).

Let's look at the method that calls the GetFilesAsync) method:

Static Async Method Start() As Task
  var imgPath := Path.Combine(Environment.GetEnvironmentVariable("userprofile"),"pictures")
  var files := await GetFilesAsync(imgPath)
  ?files:Count

This method is also different for three reasons:

  1. It starts again with the Async keyword
  2. The return data type is Task although the method does not return something.
  3. It awaits the GetFilesAsync()) method and receives a List although the return data type of that method is Task>

Example 15.2: (XS_HelloAsync.prg) ** You will find the source code in the repository

An async method (or lambda expression) runs synchronously until the first await expression. Then the method stops and waits until the asynchronous operation completes with a return value. Now comes the important part. In the meantime, the calling method continues to run. An awaited operation does not block the calling method.

A short look behind the scene

An async method runs synchronously until the first await keyword. The compiler now builds a "stub" so that the rest of the method is run through an internal callback as soon as the awaited task is complete.

Asynchronous execution is not the same as parallel execution. Imagine a cook in a restaurant. If the cook starts cooking potatoes and while the potatoes are boiled starts preparing the fish, that's asynchronous execution. Parallel execution would be if the owner hires another cook who would take care of the fish while the first cook is working on the potatoes or the salad.

A more practical example of an asynchronous method using async and await

The next example uses a method of the .Net runtime. The static CopyToAsync() method of the File class in the namespace System.IO copies a file asynchronously. So the copy operation won't block the calling method. The only extra effort is that the call has to be preceded by the await keyword (and it has to be part of an async method).

**Example 15.3: (XS_AsyncFileCopy.prg) **

// Example for how to use an asynchronous method

// public virtual System.Threading.Tasks.Task CopyToAsync (Stream destination, int bufferSize, CancellationToken cancellationToken)

using System.IO

Async Function CopyOperation() As Void
    var sourcePath := Path.Combine(Environment.CurrentDirectory, "LargeFile.txt")
    var destPath := "LargeFile2.txt"
    If File.Exists(destPath)
       File.Delete(destPath)
    EndIf
    ? "Starting async copying..."
    Begin Using var fsSource := File.Open(sourcePath, FileMode.Open)
      Begin Using var fsDest := File.Open(destPath, FileMode.Create)
        await fsSource.CopyToAsync(fsDest)
      End Using
    End Using
    ? "Finish async copying..."
    // Not possible because the copy operation is not finished yet and the file does not exists
    // ? i"Destination exists: {File.Exists(destPath)}"

Function Start() As Void
    CopyOperation()
    // Necessary to give the copy operation time to complete
    Console.ReadLine()


NOTE: Any method or function that contains an await (on the top level) has to be declared with async. The only (?) exception is the Start() function of an X# console application. If you use async without any "awaited" calls, the method will be called synchronously (and the compiler will issue a warning). But nothing bad happens.


The idea of the TAP pattern

The main idea of the Task-based Asynchronous Pattern (TAP) is that your programming style should be affected as little as possible when using asynchronous calls. Let's look at a simple succession of tasks:

Method Start() As Void
   Operation1()
   Operation2()
   Operation3()
End Method

Each method is just a metaphor for a longer-running task. Each task is executed synchronously after another. Operation2() has to wait until Operation1() had finished, Operation3() had to wait for Operation2().

Again, Operation1(), Operation2() and Operation3() are just some methods.

Turning this sequence into an asynchronous execution model is easy thanks to async and await:

Static Method Start() As Void
   var t1 := Operation1()
   var t2 := Operation2()
   var t3 := Operation3()
   Task.WaitAll(<Task>{t1, t2, t3})
End Method

Now, each of the three operations will be called asynchronously. That means, that Operation2 can start before Operation1 finishes, and Operation3 does not wait for Operation1 and Operation2 to be completed. None of the operations is blocking the next operation. There is no need for delegates or removing the asynchronous part from the method. And since none of the methods returns something (except a Task object), there is no need to assign something to a variable. Each of the methods has to be declared with Async and it must return a Task object.

Let's look into one of these methods:

Async Static Method Operation1() As Task
  ? "Starting operation 1..."
  // do not use Thread.Sleep() anymore
  Await Task.Delay(3000)
  ? "Finished operation 1..."

**Example 15.4: (XS_TAP01.prg) **

// Using async and await for long running operations
// compile with /Main:App

using System.Threading.Tasks

Class App
  Async Static Method Operation1() As Task
    ? "Starting operation 1..."
    // do not use Thread.Sleep() anymore
    Await Task.Delay(3000)
    ? "Finished operation 1..."

  Async Static Method Operation2() As Task
    ? "Starting operation 2..."
    Await Task.Delay(3000)
    ? "Finished operation 2..."

  Async Static Method Operation3() As Task
    ? "Starting operation 3..."
    Await Task.Delay(3000)
    ? "Finished operation 3..."

  Static Method Start() As Void
     var t1 := Operation1()
     var t2 := Operation2()
     var t3 := Operation3()
     Task.WaitAll(<Task>{t1, t2, t3})
  End Method

End Class

Each operation is a regular method except for two things:

  1. It has to be declared with the Async keyword
  2. The return type has to be a Task or a Task object. It can only be void if the method is an event handler.

Since each operation has no return value and would be void, the return value has to be Task.

If inside the method another blocking operation is executed, it should return a Task so that it can be awaited too:

Async Method Operation1() As Task
  await Task:Run({=> Console.WriteLine("Operation 1 Sub")})

The pros and cons of async methods

The benefits of using async methods are:

  • They improve application performance by using fewer resources and reducing the number of threads required to handle requests
  • They utilize all available cores of the CPU
  • They are usually faster than their synchronous counterparts because they do not block the current thread
  • An application can perform other tasks while waiting for data from I/O devices such as an internet connection
  • The application can process more requests in parallel without blocking threads, so the application becomes better scalable
  • Using async and await is by far the easiest way to run code in the background. This can be useful when the program needs to download many files, parse a huge number of files, or query several ports.

But as always some arguments could make you think twice about using asynchronous methods. Although the syntax is simple, debugging and maintaining asynchronous code can be more work. Especially during the learning phase where not everything might make sense.

But, as already noted, the Microsoft documentation is very good, and not surprisingly, there are tons of examples at Stackoverflow.com and in many other places too.


TIP: For an overview of the topic I recommend the excellent article by Stephen Toub who has not only written excellent articles for many years but was (or still is?) a developer on the TPL team at Microsoft:

https://devblogs.microsoft.com/dotnet/how-async-await-really-works/


Getting Started with Tasks

Instead of partially replicating the (very good) documentation, I will show a few simple examples that should help you to get started with async and await and with the Task class and its most important members.

The table summarizes the important members of the Task class with a short description. Although you will find all the members in more detail in the documentation, the overview is hopefully helpful for understanding the role of the Task class as the center of all asynchronous activities a little better.

Member Meaning
CurrentId-Property Holds the id of the current task as a static member.
Id-Property Holds the id of the current task.
IsCompleted-Property True, when the task has been completed.
Status-Property Holds the status of the task as a TaskStatus enum (values are Canceled, Running or WaitingForActivation).
Factory-Property Provides several factory methods for creating a task like StartNew()
Delay-Method Creates a task that will be completed after a given period.
Run-Method Queues an (anonymous) method to run on the thread pool and returns a Task object for the "work" (e.g. method)
Start-Method Starts the task, and returns nothing.
Wait-Method Waits for the task to complete execution.
WaitAll-Method Waits for all the provided task objects to complete execution.
WaitAny-Method Waits for any of the provided task objects to complete execution.
WhenAll-Method Creates a task that will be completed when all the provided task objects have been completed.
WhenAny-Method Creates a task that will be completed when any of the provided task objects have been completed.

Tab 15.1: Important members of the Task class with a short description

Creating new tasks

Although the Task class has a constructor that accepts an Action\<Object> argument besides other arguments (like a CancellationToken), it is more convenient to construct a started task either with the Run() method or by using Task.Factory.StartNew().

Waiting for a task

Since Tasks don't use callbacks, a consumer has to wait for a task. Each Task object contains a Wait() method. Most of the time, a program has to wait for several tasks. The Task class offers methods like WaitAll() (blocks the current thread until everything is completed) or WhenAll() (returns a Task object that represents the "action" of waiting until everything is completed) that wait for all the tasks, that are part of the array argument.

Cancellation of a task

It's easy to cancel a running task at any time. The only requirement is that the task was started with a CancellationToken as an additional argument. The first step is always to instantiate the CancellationTokenSource class (namespace System.Threading ) and use the Token property of that instance.

The following example is a "little bit" artificial since it starts as Task only to cancel it a few steps later. It also gets a little more "complicated" than usual, The recommended way of dealing with a cancellation is to add the ContinueWith() method to add another task and use the IsCanceled property to query if the task had been canceled. There is no need for exception handling.

**Example 15.5: (XS_TaskCancellation.prg) **

// An example for the cancellation of a task

using System.Threading
using System.Threading.Tasks

Function StartTask() As Task
  var cts := CancellationTokenSource{}
  var t := Task.Run(async Delegate {
      For Local i := 1 UpTo 10
        ? i"Looping the {i}th time..."
        await Task.Delay(1000)
        If i == 7
           cts:Cancel()
        EndIf
        If cts:Token:IsCancellationRequested
           cts:Token:ThrowIfCancellationRequested()
        EndIf
      Next
     }, cts:Token):ContinueWith({ task => 
       If task:IsCanceled
         ?"Zee task was cancelled prematurely!"
       EndIf
     })
    t:Wait()
  Return t

Function Start() As Void
  StartTask()

Continue with another task

Running two tasks one after another does mean that the second task only starts when the first task has completed its work. As you already know, they both run independently of each other. If Task B should only start when Task A has been completed, the ContinueWith() method of the Task object has to be used.

Although this method offers 20 overloads with many "special" arguments, there is a simple overload that takes no arguments except the Task that has been completed.

The following examples demonstrate how to continue with Task B when Task A has been completed. The example is very simple and its main purpose is to show how simple this important technique can be. The one thing to note is that the parameter t stands for the completed task.

**Example 15.6: (XS_TaskContinuation.prg) **

// An example for ContinueWith()

using System.Threading.Tasks

Class App

   Static Async Method StartTask() As Void
      await Task.Run(async Delegate {
        ?"Downloading many files with will take a long time..."
        await Task.Delay(2000)
      }):ContinueWith({ t => 
        ?"Mission accomplished (press Return) ..."
      })

End Class

Function Start() As Void
    App.StartTask()
    ?"Already at the last line because we were not waiting..."
    Console.ReadLine()

Using Web APIs asynchronously

This short chapter concludes with a nice example that shows how many possibilities we, as experienced developers, already have for many years. Can you imagine that translating any text into all major languages of the world takes only a single call? So, incorporating an advanced translator service in your application is a no-brainer.

Of course, you have to use a cloud provider (Microsoft Azure in this case) and the service is not free (actually it can be very expensive for large amounts of text). But it's a good example of what technology provides today. And it's another example of an asynchronous method call:

// Send the request and get a response
Local response := await client:SendAsync(request):ConfigureAwait(false) As HttpResponseMessage
// Read response as a string
Local result := await Response:Content:ReadAsStringAsync() As String

There are two requirements for trying out this example:

  1. You have to download the excellent Newtonsoft.json.dll, probably as part of the Nuget package. And you have to get the needed API key by signing into azure.com and setting up a new translator project. Although the service itself offers a free tier, you will need a credit card to create the Azure account itself.
  2. If you have the API key, you have to save it in a text file with the name azurekey.txt in the same directory as the prg file.

Last but not least, there are two references needed when compiling the prg file in the console window:

xsc .\XS_AsyncTranslate.prg /r:Newtonsoft.json.dll /r:System.Net.Http.dll

Example 15.7: (XS_AsyncTranslate.prg) ** You will find the source code in the repository

Downloading files from the Internet asynchronously

At last an example that seems a little more practical. It's a WinForms application that downloads the content of web pages asynchronously. The URL for each webpage is part of a text file.

I won't go into the details. I hope that the comments and the code are self-explanatory. Besides some nice (basic) programming techniques, the sample also shows how to cancel an asynchronous operation and how to report progress.

Maybe one little except from the example to illustrate how different the modern programming style with X# is like many of us are used to from the good, old 90s (and even 80s):

var taskList := UrlList:Select({ url => 
  Return DownloadAsync(url, Destinationpath)
}):ToArray()

Did you have guessed what this "one-liner" does? Right, it downloads the content of all the webpages whose urls are part of UrlList asynchronously.

UrlList is a simple array (or a generic List). Select is a LINQ extension method that returns a value for each element (URL) in that array. The return value is the return value of the DownloadAsync() method which is a Task object. When all URLs are processed, taskList contains, as the name suggests, a list of tasks. So, it becomes very simple to wait for all tasks to be completed:

Task.WhenAll(taskList)

Does anyone prefer a for loop instead? I like the code above a little better.

Alt A small WinForms example program downloads files asynchronously Fig 15.1: A small WinForms example program downloads files asynchronously