Showing posts with label TPL. Show all posts
Showing posts with label TPL. Show all posts

Thursday, February 12, 2015

Task.Run() vs. Task.Factory.StartNew()

Last night, I gave a talk on the BackgroundWorker component (one of my favorite little tools). Part of that talk is to compare the the BackgroundWorker to using Task from the Task Parallel Library. As often happens, I got a question that I didn't know the answer to off the top of my head.
What's the difference between using "Task.Factory.StartNew()" and "Task.Run()"?
The demo code I showed used "Task.Factory.StartNew()" to create a Task and start it at the same time. I knew "Task.Run()" was a bit different but didn't know how since I generally use the Factory (it was a recommendation I picked up a while back). It turns out that the difference is how much control we can take over our newly created Task.

Creating a New Task
I've been writing quite a bit about Task and await recently. But so far, this has all been from the standpoint of consuming someone else's asynchronous methods. I have plans for articles that talk about creating our own asynchronous methods, so this is jumping a little bit ahead.

There are several different ways that we can create a new Task. First, we can simply use the constructor on the Task object. The downside of this is that it does not actually *start* the task. We have to call a separate method to do that. This may actually be an upside if we want to create a Task for future use.

But often we want to create *and* start a task at the same time. To do this, we have 2 options: "Task.Run()" and "Task.Factory.StartNew()". Both of these will create a Task, start it, and then return the Task object to us. This lets us assign it to a variable to keep track of the task, check for exceptions, or add continuations.

But one of these gives us a bit more control. Let's take a closer look.

Options with Task.Run()
We'll start by looking at the overloads that we have available when we use the "Task.Run()" method. Here is one of the most basic overloads:


This takes delegate as a parameter, in this case Func<TResult> -- a method that takes no parameters and returns a value (if you want more information of Func, check out my video series on Delegates). For our example, we're assuming that we want to return an integer value.

The most complex version of this method takes 2 parameters:


Here we can see that in addition to the delegate, we have another parameter for a cancellation token. And these are really the only options.

If we look at the other overloads, we see that there are a variety of different delegate types (some that return values, some that return Tasks, some that return void), but they are all very similar. Here's the list from the documentation:


Our only real choice for adding more information is to include a cancellation token.

Options with Task.Factory.StartNew()
If we look at the overloads for "Task.Factory.StartNew()", we'll see a number of other options that let us take more control over our Task.

Here's the basic version:


Just like "Task.Run()", this particular overload takes a delegate as a parameter. And whether we use "Task.Run(Func<int> function)" or "Task.Factory.StartNew(Func<int> function)", there is absolutely no difference in functionality. A Task would be created, it would be started (meaning the delegate would be invoked), and we would get a Task back as a return value that we can assign to a variable.

[Update: As Dan points out in the comments, there are differences in the defaults that are used for TaskCreationOptions and the TaskScheduler (mentioned below) depending on whether we use Task.Run() or Task.Factory.StartNew(). There are also some subtleties when it comes to "unwrapping" tasks. These differences become important when we create more complex Tasks. More details from Stephen Toub: Task.Run vs Task.Factory.StartNew.]

But "Task.Factory.StartNew()" has many more options. Here's the most complex overload of the method:


Lots of stuff to look at here.

object state
First, notice the 2nd parameter is an "object state". This allows us to pass an object that we can use as a parameter for our delegate. Notice that the 1st parameter is "Func<object,int>". This means that we have a delegate that takes an "object" as a parameter and returns an "int" value. The "object" parameter of the delegate comes from the "object state" parameter that we pass in.

CancellationToken
The next parameter is a CancellationToken. This is not much different from what we have in the "Task.Run()" options, and as you can imagine this allows us to pass in a token that is used for cancellation.

For a little more information on cancellation tokens, check out Task and await: Basic Cancellation.

TaskCreationOptions
The next parameter is where things start to get interesting: TaskCreationOptions. This is an enum that has the following values:


We won't go into the details here, but you can see just from the names that we can provide more information for our Task and how we want it to behave. We'll look at the specifics in a later article. For now, you can check out the MSDN article for descriptions of each option: TaskCreationOptions Enumeration.

TaskScheduler
The last parameter that we have is a TaskScheduler. Again, we won't go into the details of all of the options here, but you can take a look at the MSDN article on the TaskScheduler Class for an example.

One of the most common uses for this parameter is to use the current synchronization context. We actually took a look at this back when in the first article Task and Await: Consuming Asynchronous Methods.

In that article, we create a continuation -- a method that we want to run when our task is complete. But we initially ran into a problem because we also want to update UI controls. By default, the continuation was not running on the UI thread, but when we added a TaskScheduler as a parameter, we were able to get things to work:

From Task.ContinueWith in prior article

When we ask for a TaskScheduler "FromCurrentSynchronizationContext", it will use the synchronization context from where we called this method. Since the "ContinueWith" method is being called from the UI thread, it uses the synchronization context from that same thread. This means that we can access the UI controls from within our continuation without running into any problems.

So, just like with our continuation, we could use the UI context when we create a new Task if we need to interact with UI controls. Now, I would *not* recommend interacting with UI controls in a Task that we're trying to get off of the UI thread. There are other ways of getting information back to our UI to use (such as through progress reporting). We'll take a look at this in a future article as well.

Other Options
There are a total of 16 overloads for "Task.Factory.StartNew()" that combine these parameters in different ways. Here are the overloads from Help:


So, lots of options.

Wrap Up
So whether we use "Task.Run()" or "Task.Factory.StartNew()" really depends upon how much control we need to take over our Task. Personally, I've created a habit of always using "Task.Factory.StartNew()". But it looks like I'll need to reconsider this by looking at the defaults for each method and the other recommendations that are out there.

In future articles, we'll take a closer look at creating our own Tasks and when we might need to use these various options.

In the meantime, don't be afraid to ask questions. And don't be afraid to say "I don't know" in answer to a question. I do that all the time -- it's impossible to know everything, but we can always learn something new.

Happy Coding!

Tuesday, January 13, 2015

Task and Await: Basic Cancellation

In our exploration of Task, await, and asynchronous programming, we want to take a look at the concept of cancellation. When we kick off a long-running process, we may decide that we want to cancel the operation before it completes.

We're still looking at things from the consumer side: methods that make calls to asynchronous methods. In future articles, we'll see how to write our own async methods.

We'll continue with our previous example project which is available on GitHub: https://github.com/jeremybytes/using-task. The articles that describe the project up to this point are available here: Exploring Task, Await, and Asynchronous Methods.

The completed code for this article is in the 09-CancellationBasics branch.

So let's jump into some code!

Cancellation in the Asynchronous Method
For our asynchronous method, we're using the "Get()" method on the "PersonRepository" class. This method does not support cancellation. So we'll add a parameter to accept a "CancellationToken" and use it in our code:

from PersonRepository.cs

There are a couple of things to note here. First, we have a default value for our "cancellationToken" parameter. This is because we don't want to break our existing method calls that don't pass a cancellation token.

In this case, if the "Get()" method is called without any parameters, then the "cancellationToken" will be a instantiated with the CancellationToken constructor. Now, as we saw when we looked at "UI Considerations When Using Asynchronous Methods", calling the constructor like this will create a cancellation token that is in the "not canceled" state.

Also, as we saw in the update in that same article, we swapped that out to "CancellationToken.None". Unfortunately, that's not something that we can use for a default parameter assignment, so we have to use the constructor here.

The second thing to note is that after waiting for 3 seconds, we call "ThrowIfCancellationRequested()" on our cancellation token. This does the following:
  • It sets the "IsCanceled" and "Status" properties on our Task to "true" and "TaskStatus.Canceled", respectively.
  • It throws an exception. This is a special exception that is automatically handled (sort of).
  • It does not set the "IsFaulted" state on our Task.
  • It does not add the exception to the AggregateException of the Task (the property we saw when we looked at exception handling).
We'll dive into this topic a bit deeper when we get into creating our own asynchronous methods. For now, let's head back to our calling code.

Cancelling a Method with Task
Here's how we left our method that interacts with the Task returned from the "Get()" method:


After getting a Task back from the "Get()" method, we add a continuation that runs on the UI thread. This currently checks for a faulted state (if there's an exception thrown) and checks for successful completion.

To test the cancellation functionality, we'll just hard-code a cancellation token:


When we pass in "true" to the CancellationToken constructor, it creates a cancellation token that is in the "cancellation requested" state. (We'll get rid of this hard-coded value a bit later).

Cancellation Requested
So now when we run our application and click our "Task" button, it will call the "Get" method with cancellation requested.

One thing that is very important to note about using cancellation tokens is that they do not directly affect processing -- meaning, they don't stop operations or cause an abort. Instead, the cancellation token is more of a flag; it gives us a way to let the asynchronous method know that we want to cancel the operation, but it's up to the asynchronous method itself to decide how to handle this request.

In our case, the "Get()" method will still have the 3 second delay. This is because the delay is in the method before we check the cancellation token at all. After the delay is done, then the method checks the cancellation token state and throws the exception if cancellation is requested.

Cancellation Functionality
So let's run our application to see what happens. When we click the button, it is disabled while we wait:


Then the button is re-enabled:


Nothing else happens. And this makes sense if we review our method. We check for "faulted" and show a message box. We check for "RanToCompletion" and populate the list box. But we don't have anything that checks for the "canceled" status. So we end up skipping both of the current "if" blocks and just re-enable the button.

Checking for Cancellation
It's easy enough to add functionality to check for cancellation. When we updated our continuations, we saw that Task has an "IsCanceled" property. We can easily use this in our continuation.


If "IsCanceled" it true, then show a message to the user. And when we run our application, we see just that:


Cleaning Things Up
Now our code is getting a bit hard to read. One thing we can do to keep things consistent is to check the "Status" property for all 3 of our "if" blocks (we saw the TaskStatus options when we updated our continuations as well).


This is a little bit better. At least we're checking the same property in all of our options now. But we can take this a step further and turn this into a "switch" statement:


To me, this is much easier to follow. We can see what happens with each of the different states. And it's actually a bit more correct, because we probably wanted to have "if / else if / else" in our previous code to make things mutually exclusive. With this "switch" statement, this is much more clear.

Note: There are a lot of other possible values for Status, but we're sticking with just the ones that we care about here. These are the statuses that we get after a Task has stopped running, which is the state that we should have since we are in a continuation.

Hard-Coded Cancellation
This is fine for test code, but we probably don't want to leave the hard-coded cancellation in place. We'll want to make it so that we can cancel from the UI.

But before we do that, let's get our "await" method working with cancellation.

Cancelling a Method with Await
Here's how we left our method that uses "await":


And we can change the "Get()" call so that is passed in a hard-coded cancellation token just like we did with our Task method.


Now let's run the application to see what happens.


Interesting. We get a message box. This means that it looks like we're hitting the "catch" block of our method. Let's verify that by setting a breakpoint and checking the exception:


This shows that we do hit the "catch" block, and the exception is an "OperationCanceledException". So when we use the "await" keyword, the special cancellation exception gets surfaced on our UI thread.

But since this is a specific exception, it's easy enough to add another "catch" block just for this:


Now we have a "catch" block that just handles the cancellation and another "catch" that handles all of our other exceptions.

When we run our application, we see the new message box (note the "Canceled" in the caption):


As usual, we see that things are a bit easier to deal with when we use "await" rather than dealing with "Task" directly. So if we have a situation where we can "await" a method, that code will be a bit easier to read and write.

Hard-Coded Cancellation
As with our "Task" method, our "await" method has a hard-coded cancellation token. This is fine for testing our functionality, but it's not what we want in our final application. Instead, we want to give our users a way to cancel the operation from the application while it is running.

Managing a Cancellation Token
The first thing that we'll do is add another button to our UI. This will be a cancellation button, and we want this to be disabled when our application starts. That's because we only want our button to be enabled when we have something that can be canceled.

Here's our updated UI:


CancellationTokenSource
Now we might be inclined to create a CancellationToken and then set its value when our cancel button is clicked. But this doesn't work. When I was first working with "Task", this is what I struggled with.

The problem is when we create a CancellationToken directly, it is not modifiable. We can check its state, but this is a read-only property. And there are no methods for changing the state.

Instead, we need to create a CancellationTokenSource. This is an object that we can use to manage a cancellation token.

We'll create a class-level field for our CancellationTokenSource. This way, we'll be able to access the object inside our various methods of our class:


We're just declaring the field here. We'll see where we instantiate it in just a bit.

Setting the Token to Canceled
Inside the "Click" event handler for our cancel button, we'll set our token to a canceled state:


This will set the "IsCancellationRequested" property of the cancellation token to "true". I would highly recommend taking a look at the methods and properties on both CancellationTokenSource and CancellationToken. There are a lot of options available. We're just using a few of the methods and properties here.

The other thing that we do in the method is disable the "Cancel" button. If we click it once, there's no reason for us to click it again. As a reminder, our method will not immediately return; we still need to wait for that 3 second delay to complete before the cancellation token is checked. By disabling the button, we give the user a visual clue that it was actually clicked.

Using the Token with Task
Now that we have a CancellationTokenSource, we can start to update our methods. We'll start with the method that uses Task.

Here are the bulk of the changes:


First, we create a new instance of the CancellationTokenSource and assign that to our field. The reason that I'm doing this is because we'll have similar code in the "await" method, and we're sharing the "tokenSource" field. I want to make sure that we get a new token source (which has an uncanceled token) at the start of each method.

Next, we enable the "CancelButton". This will light up the button in the UI so that we can request cancellation.

Then we pass the "Token" property of our CancellationTokenSource to our "Get" method. The "Token" property is a cancellation token, and this is how we get our live token to our asynchronous method.

Finally, at the bottom of our method, we disable the cancellation button in the UI (since the process is complete):


Running the Code
With this in place, let's run our application to see what happens. After we click the "Fetch" button, we see that the "Cancel" button is enabled:


Then if we click the "Cancel" button before our 3 seconds is up, we see that the "Cancel" button is disabled, but we're still waiting on the "Get()" method.


Then we get the cancellation message that we saw earlier:


After we clear this message, the "Fetch" button will re-enable just like we saw previously.

And if we do not cancel, things still run as expected:


Final Task Method
Here's our final method that uses "Task" with the cancellation functionality in place:


Now let's take a look at our "await" method.

Using the Token with Await
As you can imagine, we'll do something similar with our "await" method: use the CancellationTokenSource and enable/disable buttons as appropriate. Here's the final method:


Just like with our "Task" method, we create a new instance of the "CancellationTokenSource". This will ensure that things are set back to an initial state. We use the "Token" property as a parameter for the "Get" method. And we just enable our "Cancel" button and disable it again in our "finally" block.

And as you can imagine, this functions similar to our other method. First, when we click the "Fetch" button, our "Cancel" button will enable:


Then if we click our "Cancel" button, it will be disabled while we wait for the "Get()" method.


Then we get our cancellation message.


And, of course, our functionality works just the same as before if we do not cancel it.

Wrap Up
The biggest stumbling block I had when I started working with "Task" was understanding that you couldn't use a "CancellationToken" directly. Instead, we need to work with a "CancellationTokenSource". This object has a property, "Token", which is a cancellation token. And we set the state of the token by calling methods on the CancellationTokenSource.

And as we saw, when an asynchronous method is canceled, we need to pay attention to the state of the Task that returns. If we're using "Task" directly, then we can check the "Status" or the "IsCanceled" properties to know if the Task has been canceled.

And if we're using "await", then we can check for an "OperationCanceledException". This will let us know that the operation was canceled before it ran to completion.

Overall, canceling an asynchronous method is not all that complex. But there are a few things that aren't obvious. By doing some exploration, we can see how things work and how we can take advantage of these features in our own applications.

Happy Coding!

Wednesday, January 7, 2015

Task Continuations: Checking IsFaulted, IsCompleted, and TaskStatus

When we handle Task continuations manually, we get a lot of flexibility. Previously, we saw how we could create multiple continuations that always run, only run on successful completion, and only run if there's an exception (Task and Await: Basic Exception Handling).

But we have alternatives. We can consolidate the code into a single continuation and then use properties on the Task itself to determine what to do. And that's what we'll look at today. Along the way, we'll look at the "IsFaulted", "IsCompleted", and "Status" properties of our Task.

All of the articles in this series are collected here: Exploring Task, Await, and Asynchronous Methods. And the code is available on GitHub: https://github.com/jeremybytes/using-task.

The completed code for this article is in the 08-AlternateContinuation branch.

[Update 09/2015: For a video version of this article, look here: IsFaulted, IsCompleted, and Task.Status.]

How We Left the Code
As a reminder, here's how we left our method that directly interacts with Task:


This calls the asynchronous "repository.Get()" method. Then we take the Task that comes back from this method and set up 3 continuations.

Let's break these down.

Continuation #1: Success State


The first continuation has the option for "OnlyOnRanToCompletion". This means that this will only run if the Task completes successfully. In that case, we get the "Result" and use it to populate the list box in our UI.

Continuation #2: Faulted State


The second continuation has the option for "OnlyOnFaulted". This means that this will only run if the Task throws an exception. If that's the case, we display any exceptions in a message box. But we could just as easily rethrow the exceptions on the UI thread, log them, or display them to the user in a different way.

Continuation #3: Any State


The third continuation does not have any continuation options. So it will run regardless of the final state of the Task. We use this continuation to reset our button to the "enabled" state.

A Single Continuation
As mentioned, Task is extremely flexible. That means we can handle this functionality in a bit of a different way. Instead of creating continuations that have "TaskContinuationOptions", we can create a single task and simply use properties on the Task itself to decide which pieces of code we want to run.

A Continuation That Always Runs
We'll take our first Task continuation (the "Success State") and combine the functionality from the other continuations. We will want this continuation to run regardless of the final state, so we'll start by removing the continuation option parameter:


We've also removed the cancellation token parameter. We no longer need it since we have an overload of "ContinueWith" that has just "Action" and "TaskScheduler" as parameters.

Rolling in the Other Continuation Code
Next, we'll copy the body of our second continuation (the "Exception State") above our current code.


Then we'll copy in the body of our last continuation (the "Any State") at the bottom.


Checking the IsFaulted Property
Now we have all of our code in a single continuation. But we have a bit of a problem. What happens if we run the application, and the Task does *not* throw an exception?


We get a runtime error. Since the Task is not faulted, the "Exception" property is null. So we get a "NullReferenceException" when we try to access it.

Fortunately, Task has a property called "IsFaulted" that will tell us whether our Task has exceptions. We can use this to wrap our error handling code.


When "IsFaulted" is true, we know that the "Exception" property is populated, and we can safely access it and deal with our faulted state.

But what if our Task *does* throw an exception?


Now we have a problem when we try to access the "Result" property. This shouldn't be a surprise. This is exactly the same behavior we saw when we were looking at Basic Exception Handling before we added the continuation options.

So, we only want this code to run if the Task is *not* faulted. That's easy enough with an "else" statement.


What About Cancellation?
There is one other possibility that we need to consider here: cancellation. Now, we haven't covered cancellation yet (we'll spend plenty of time on that in a future article), but we need to be aware that the Task may be canceled.

If the Task is in a canceled state, then the "Result" property is not valid, and we will get an exception if we try to access it. So simply having the "else" statement here isn't quite good enough.

Now in addition to "IsFaulted", we have some other properties on our Task, including "IsCanceled" and "IsCompleted".

Now we might be tempted to use the "IsCompleted" property the same way that we used "IsFaulted". That would look something like this:


But what we'll find is this does not work. To understand why, let's take a look at the Task properties in Help:


Notice that all three properties state that the Task has completed. This means that if we get an exception in the Task, both "IsFaulted" and "IsCompleted" will both be true.
We cannot use "IsCompleted" to mean "completed successfully". "IsCompleted" really just means "is no longer running".
TaskStatus to the Rescue
Fortunately for us, there is another property that we can use for this purpose. Task has a "Status" property which is of type "TaskStatus". This is an enum with the following values (from Help):


We can use this property to check the "Canceled" or "Faulted" state of the Task. We can also use it to see if the Task is currently running or waiting to run. But the one that we care about here is "RanToCompletion".

"RanToCompletion" is the equivalent of the continuation option that we used initially: "OnlyOnRanToCompletion". So let's use this in our code:


Now our code behaves as expected. If the Task throws an exception, only the first "if" statement will be true, and our error handling code will run. If the task does not throw an exception and it completes successfully, then only the second "if" statement will be true, and our code that loads the list box will run.

And regardless of whether the Task completes successfully or is faulted, the button will be re-enabled. (Just to be safe, we could wrap this in a try/finally block like we did with the "await" method, but we won't worry about that for now).

Note: Instead of using "IsFaulted", we could check for "TaskStatus.Faulted". And since "TaskStatus" is an enum, it would be very easy to set up a "switch" statement to check the different states. Ultimately how we implement the code will depend on the level of complexity and what makes the code easiest to read.

Success State Output
Let's run the application. To start with, we'll use the "success" state. For this, we'll check the "Get" method in the "PersonRepository" class (part of the "UsingTask.Library" project) and make sure that the exception is *not* being thrown.

PersonRepository.cs

When we click the button, we see the button is initially disabled:


Then after our Task completes, the list box is populated and the button is re-enabled:


Looks good so far.

Exception State Output
Now let's update our "Get" method so that is *does* throw an exception.


When we click the button, it is initially disabled:


Then the message box shows the exception:


Notice that the button is still disabled. This is because the modal dialog stops the rest of the code in the continuation from processing.

As soon as we clear the dialog, the button is re-enabled:


So we have *almost* the same behavior that we had before. When we had the separate continuations, the button was re-enabled at the same time as the error message was displayed. That's because both continuations kicked off at the same time.

But since we have a single continuation now, the button is not re-enabled until after we clear the error message. This matches the behavior that we saw in our "await" method in the prior article.

Final Method
Let's take a look at our final method.


Instead of having 3 continuations, we have a single continuation that runs regardless of how the Task completed (success, error, or cancellation). We use properties on the Task itself to handle the different states.

As a reminder, the completed code for this article is in the 08-AlternateContinuation branch.

Which Approach Should We Use?
Ultimately, whether we choose to have a single continuation or multiple continuations is up to us. As we've seen, Tasks are extremely flexible. If we have a fairly simple set of completion code (as we have here), then we may want to keep things in a single continuation. Personally, I think this code is a bit easier to follow than the code that we started with.

But if our needs are more complex, we may want to keep the continuations separate. For example, we could have continuations that kick off additional child tasks. Or we could have continuations that have their own continuations. In those situations, we may want to keep things separate so it is easier to follow the flow of each operation.

Choices are good, but sometimes they can feel a bit overwhelming. My approach is to generally start out as simple as possible -- keeping things easy to read. As complexity builds, then I split things out into different units -- keeping things fairly small and (hopefully) easy to follow.

When we have multiple options available to us, we can pick the one that best fits our needs and the needs of our application.

Happy Coding!