C Cancel Task and Start It Up Again

Canceling Tasks and Handling Exceptions

Another mutual requirement of applications that perform long-running operations is the ability to stop those operations if necessary. However, you should not simply abort a job because this could leave the data in your application in an indeterminate state. Instead, the TPL implements a cooperative cancellation strategy. Cooperative cancellation enables a job to select a convenient bespeak at which to stop processing and likewise enables it to undo any work information technology has performed prior to counterfoil if necessary.

The Mechanics of Cooperative Cancellation

Cooperative counterfoil is based on the notion of a counterfoil token. A cancellation token is a structure that represents a request to cancel one or more tasks. The method that a task runs should include a System.Threading.CancellationToken parameter. An awarding that wants to abolish the task sets the Boolean IsCancellationRequested property of this parameter to true. The method running in the task tin can query this belongings at various points during its processing. If this property is gear up to true at whatsoever point, it knows that the application has requested that the task be canceled. Also, the method knows what work it has done so far, so information technology can undo any changes if necessary so finish. Alternatively, the method tin can simply ignore the request and go on running if information technology does not want to cancel the job.

An application obtains a CancellationToken by creating a System.Threading.CancellationTokenSource object and querying the Token belongings of this object. The awarding tin can so laissez passer this CancellationToken object as a parameter to any methods started past tasks that the application creates and runs. If the application needs to abolish the tasks, information technology calls the Cancel method of the CancellationTokenSource object. This method sets the IsCancellationRequested property of the CancellationToken passed to all the tasks.

The following code instance shows how to create a cancellation token and utilize it to cancel a chore. The initiateTasks method instantiates the cancellationTokenSource variable and obtains a reference to the CancellationToken object available through this variable. The code then creates and runs a job that executes the doWork method. Subsequently on, the code calls the Cancel method of the cancellation token source, which sets the cancellation token. The doWork method queries the IsCancellationRequested holding of the cancellation token. If the property is fix the method terminates; otherwise, information technology continues running.

public class MyApplication {     ...     // Method that creates and manages a task     private void initiateTasks()     {         // Create the cancellation token source and obtain a cancellation token         CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();         CancellationToken cancellationToken = cancellationToken.Token;         // Create a task and start it running the doWork method         Task myTask = Task.Manufacturing plant.StartNew(() => doWork(cancellationToken));         ...         if (...)         {             // Cancel the task             cancellationTokenSource.Abolish();         }         ...    }     // Method run by the task    private void doWork(CancellationToken token)    {        ...        // If the application has gear up the cancellation token, cease processing        if (token.IsCancellationRequested)        {             // Tidy up and end            ...            return;        }        // If the task has not been canceled, continue running as normal        ...    } }

As well every bit providing a high degree of control over the cancellation processing, this approach is scalable across any number of tasks. You tin can offset multiple tasks and pass the same CancellationToken object to each of them. If yous call Abolish on the CancellationTokenSource object, each task volition see that the IsCancellationRequested belongings has been ready and tin react accordingly.

You can also register a callback method with the cancellation token past using the Register method. When an application invokes the Cancel method of the respective CancellationTokenSource object, this callback runs. Nevertheless, you cannot guarantee when this method executes; it might be before or after the tasks have performed their ain counterfoil processing, or fifty-fifty during that process.

... cancellationToken,Annals(doAdditionalWork); ... individual void doAdditionalWork() {     // Perform additional cancellation processing }

In the next do, you lot will add cancellation functionality to the GraphDemo application.

Add together cancellation functionality to the GraphDemo awarding

  1. Using Visual Studio 2010, open the GraphDemo solution, located in the \Microsoft Printing\Visual CSharp Step By Pace\Affiliate 27\GraphDemo Canceling Tasks folder in your Documents binder.

    This is a completed copy of the GraphDemo application from the previous exercise that uses tasks and threads to improve responsiveness.

  2. In Solution Explorer, in the GraphDemo project, double-click GraphWindow.xaml to brandish the form in the Design View window.

  3. From the Toolbox, add a Button control to the grade under the elapsing label. Align the button horizontally with the plotButton push. In the Properties window, alter the Name holding of the new push button to cancelButton, and change the Content holding to Cancel.

    The amended class should look like the following paradigm.

  4. Double-click the Cancel push button to create a Click upshot handling method chosen cancelButton_Click.

  5. In the GraphWindow.xaml.cs file, locate the getDataForGraph method. This method creates the tasks used by the application and waits for them to complete. Move the announcement of the Task variables to the course level for the GraphWindow class equally shown in bold in the following code, and then modify the getDataForGraph method to instantiate these variables:

    public fractional course GraphWindow : Window {      ...                              private Task first, second, 3rd, fourth;              ...      private byte[] getDataForGraph(int dataSize)     {         byte[] information = new byte[dataSize];         outset = Task.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / 8));         2d = Task.Manufactory.StartNew(() => generateGraphData(data, pixelWidth / 8, pixelWidth / 4));         3rd = Task.Manufacturing plant.StartNew(() => generateGraphData(data, pixelWidth / 4, pixelWidth * three / 8));         quaternary = Job.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / 8, pixelWidth / 2));         Task.WaitAll(offset, second, third, fourth);         return information;    } }
  6. Add together the following using statement to the listing at the top of the file:

    using System.Threading;

    The types used by cooperative cancellation live in this namespace.

  7. Add a CancellationTokenSource member called tokenSource to the GraphWindow class, and initialize it to null, every bit shown here in bold:

    public class GraphWindow : Window {     ...     private Task first, second, 3rd, 4th;                              private CancellationTokenSource tokenSource = nothing;              ... }
  8. Find the generateGraphData method, and add together a CancellationToken parameter called token to the method definition:

    private void generateGraphData(byte[] data, int partitionStart, int partitionEnd,              CancellationToken token)              {      ... }
  9. In the generateGraphData method, at the start of the inner for loop, add together the code shown adjacent in bold to check whether cancellation has been requested. If and so, return from the method; otherwise, continue calculating values and plotting the graph.

    private void generateGraphData(byte[] data, int partitionStart, int partitionEnd, CancellationToken token) {     int a = pixelWidth / 2;     int b = a * a;     int c = pixelHeight / 2;      for (int x = partitionStart; 10 < partitionEnd; ten ++)     {         int southward = ten * x;         double p = Math.Sqrt(b - s);         for (double i = -p; i < p; i += three)         {                              if (token.IsCancellationRequested)                              {                              return;                              }              double r = Math.Sqrt(due south + i * i) / a;             double q = (r - i) * Math.Sin(24 * r);             double y = i / 3 + (q * c);             plotXY(data, (int)(-x + (pixelWidth / 2)), (int)(y + (pixelHeight / 2)));             plotXY(data, (int)(10 + (pixelWidth / ii)), (int)(y + (pixelHeight / ii)));        }    } }
  10. In the getDataForGraph method, add together the following statements shown in assuming that instantiate the tokenSource variable and retrieve the CancellationToken object into a variable chosen token:

    private byte[] getDataForGraph(int dataSize) {     byte[] data = new byte[dataSize];                              tokenSource = new CancellationTokenSource();                              CancellationToken token = tokenSource.Token;              ... }
  11. Change the statements that create and run the 4 tasks, and pass the token variable as the final parameter to the generateGraphData method:

    start = Task.Mill.StartNew(() => generateGraphData(data, 0, pixelWidth / 8,              token));              2nd = Chore.Factory.StartNew(() => generateGraphData(data, pixelWidth / 8, pixelWidth / 4,                token));              3rd = Chore.Factory.StartNew(() => generateGraphData(data, pixelWidth / 4, pixelWidth * iii / 8,                token));              4th = Task.Factory.StartNew(() => generateGraphData(information, pixelWidth * 3 / 8, pixelWidth / 2,                token));            
  12. In the cancelButton_Click method, add the code shown here in assuming:

    private void cancelButton_Click(object sender, RoutedEventArgs east) {                              if (tokenSource != null)                              {                              tokenSource.Abolish();                              }              }

    This code checks that the tokenSource variable has been instantiated; if it has been, the code invokes the Abolish method on this variable.

  13. On the Debug menu, click Offset Without Debugging to build and run the awarding.

  14. In the GraphDemo window, click Plot Graph, and verify that the graph appears as it did before.

  15. Click Plot Graph again, and and so quickly click Cancel.

    If you are quick and click Cancel before the data for the graph is generated, this activity causes the methods being run past the tasks to return. The data is not consummate, so the graph appears with holes, as shown in the following effigy. (The size of the holes depends on how quickly y'all clicked Cancel.)

  16. Close the GraphDemo window, and return to Visual Studio.

You lot can determine whether a task completed or was canceled by examining the Status property of the Task object. The Condition property contains a value from the Organization.Threading.Tasks.TaskStatus enumeration. The following list describes some of the status values that you might normally encounter (there are others):

  • Created This is the initial state of a task. It has been created but has not yet been scheduled to run.

  • WaitingToRun The task has been scheduled but has not yet started to run.

  • Running The chore is currently being executed by a thread.

  • RanToCompletion The job completed successfully without whatever unhandled exceptions.

  • Canceled The task was canceled before information technology could start running, or it acknowledged cancellation and completed without throwing an exception.

  • Faulted The job terminated considering of an exception.

In the next exercise, you will endeavor to report the status of each job so that y'all tin can see when they have completed or accept been canceled.

Brandish the condition of each chore

  1. In Visual Studio, in the Lawmaking and Text Editor window, find the getDataForGraph method.

  2. Add the following code shown in bold to this method. These statements generate a cord that contains the status of each task after they have finished running, and they display a bulletin box containing this string.

    private byte[] getDataForGraph(int dataSize) {     ...     Chore.WaitAll(offset, second, 3rd, fourth);                              Cord message = Cord.Format("Condition of tasks is {0}, {1}, {2}, {three}",                              first.Status, second.Status, 3rd.Status, fourth.Status);                              MessageBox.Prove(message);              return data; }
  3. On the Debug bill of fare, click Start Without Debugging.

  4. In the GraphDemo window, click Plot Graph simply do not click Cancel. Verify that the following bulletin box appears, which reports that the condition of the tasks is RanToCompletion (four times), and so click OK. Notation that the graph appears simply subsequently you lot have clicked OK.

    httpatomoreillycomsourcemspimages1374372.png

  5. In the GraphDemo window, click Plot Graph once more and then chop-chop click Abolish.

    Surprisingly, the message box that appears still reports the condition of each job as RanToCompletion, fifty-fifty though the graph appears with holes. This is because although you sent a cancellation request to each job by using the counterfoil token, the methods they were running just returned. The .Net Framework runtime does not know whether the tasks were actually canceled or whether they were allowed to run to completion and simply ignored the counterfoil requests.

  6. Close the GraphDemo window, and return to Visual Studio.

Then how do you bespeak that a task has been canceled rather than allowed to run to completion? The respond lies in the CancellationToken object passed as a parameter to the method that the task is running. The CancellationToken grade provides a method chosen ThrowIfCancellationRequested. This method tests the IsCancellationRequested belongings of a cancellation token; if it is true, the method throws an OperationCanceledException exception and aborts the method that the chore is running.

The application that started the thread should be prepared to catch and handle this exception, only this leads to another question. If a chore terminates past throwing an exception, it actually reverts to the Faulted state. This is true, even if the exception is an OperationCanceledException exception. A chore enters the Canceled country just if information technology is canceled without throwing an exception. Then how does a job throw an OperationCanceledException without it being treated as an exception?

The answer lies in the task itself. For a job to recognize that an OperationCanceledException is the result of canceling the job in a controlled manner and not but an exception caused past other circumstances, information technology has to know that the functioning has actually been canceled. It can do this simply if it can examine the cancellation token. You passed this token equally a parameter to the method run by the task, just the task does not really look at whatsoever of these parameters. (It considers them to be the business of the method and is not concerned with them.) Instead, you specify the counterfoil token when y'all create the task, either as a parameter to the Task constructor or as a parameter to the StartNew method of the TaskFactory object you are using to create and run tasks. The following code shows an instance based on the GraphDemo application. Observe how the token parameter is passed to the generateGraphData method (as earlier), but likewise every bit a separate parameter to the StartNew method:

Chore first = null; tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; ... get-go = Job.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / eight, token),          token);        

Now when the method being run by the task throws an OperationCanceledException exception, the infrastructure behind the chore examines the CancellationToken. If information technology indicates that the job has been canceled, the infrastructure handles the OperationCanceledException exception, acknowledges the cancelation, and sets the status of the task to Canceled. The infrastructure and then throws a TaskCanceledException, which your awarding should exist prepared to catch. This is what you lot will do in the next do, but before you do that you need to larn a footling more virtually how tasks heighten exceptions and how you should handle them.

Handling Job Exceptions past Using the AggregateException Class

Yous have seen throughout this book that exception handling is an important element in any commercial application. The exception treatment constructs you have met and so far are straightforward to apply, and if you use them carefully it is a unproblematic thing to trap an exception and determine which piece of lawmaking raised it. However, when you start dividing work into multiple concurrent tasks, tracking and handling exceptions becomes a more complex problem. The issue is that different tasks might each generate their ain exceptions, and you demand a manner to take hold of and handle multiple exceptions that might be thrown concurrently. This is where the AggregateException class comes in.

An AggregateException acts equally a wrapper for a collection of exceptions. Each of the exceptions in the drove might be thrown by different tasks. In your application, you can take hold of the AggregateException exception and then iterate through this drove and perform any necessary processing. To help you, the AggregateException class provides the Handle method. The Handle method takes a Func<Exception, bool> consul that references a method. The referenced method takes an Exception object as its parameter and returns a Boolean value. When you call Handle, the referenced method runs for each exception in the collection in the AggregateException object. The referenced method can examine the exception and take the appropriate action. If the referenced method handles the exception, it should return truthful. If non, it should return false. When the Handle method completes, any unhandled exceptions are arranged together into a new AggregateException and this exception is thrown; a subsequent outer exception handler can and then grab this exception and procedure information technology.

In the next practise, y'all volition see how to catch an AggregateException and utilize it to handle the TaskCanceledException exception thrown when a task is canceled.

Acknowledge cancellation, and handle the AggregateException exception

  1. In Visual Studio, display the GraphWindow.xaml file in the Pattern View window.

  2. From the Toolbox, add a Characterization control to the form underneath the cancelButton push button. Align the left edge of the Label control with the left border of the cancelButton button.

  3. Using the Backdrop window, change the Name belongings of the Characterization control to status, and remove the value in the Content holding.

  4. Return to the Code and Text Editor window displaying the GraphWindow.xaml.cs file, and add the post-obit method below the getDataForGraph method:

    private bool handleException(Exception e) {     if (e is TaskCanceledException)     {         plotButton.Dispatcher.Invoke(new Action(() =>         {             condition.Content = "Tasks Canceled";         }), DispatcherPriority.ApplicationIdle);         return true;     }     else     {         return false;     } }

    This method examines the Exception object passed in as a parameter; if information technology is a TaskCanceledException object, the method displays the text "Tasks Canceled" in the status characterization on the form and returns truthful to point that it has handled the exception; otherwise, information technology returns false.

  5. In the getDataForGraph method, change the statements that create and run the tasks and specify the CancellationToken object equally the 2nd parameter to the StartNew method, as shown in assuming in the following code:

    private byte[] getDataForGraph(int dataSize) {     byte[] data = new byte[dataSize];     tokenSource = new CancellationTokenSource();     CancellationToken token = tokenSource.Token;      ...     showtime = Task.Factory.StartNew(() => generateGraphData(information, 0, pixelWidth / 8, token),                token);     2d = Task.Manufacturing plant.StartNew(() => generateGraphData(information, pixelWidth / 8, pixelWidth / iv, token),                token);     third = Task.Manufactory.StartNew(() => generateGraphData(information, pixelWidth / 4, pixelWidth * 3 / viii, token),                token);     quaternary = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / eight, pixelWidth / 2, token),                token);     Job.WaitAll(first, 2d, 3rd, fourth);     ... }
  6. Add a endeavor cake effectually the statements that create and run the tasks, and wait for them to complete. If the look is successful, display the text "Tasks Completed" in the status label on the form past using the Dispatcher.Invoke method. Add a catch block that handles the AggregateException exception. In this exception handler, call the Handle method of the AggregateException object and pass a reference to the handleException method. The code shown side by side in bold highlights the changes you should make:

    private byte[] getDataForGraph(int dataSize) {     byte[] data = new byte[dataSize];     tokenSource = new CancellationTokenSource();     CancellationToken token = tokenSource.Token;                              endeavor                              {              first = Task.Manufacturing plant.StartNew(() => generateGraphData(data, 0, pixelWidth / 8, token), token);         2nd = Task.Manufactory.StartNew(() => generateGraphData(information, pixelWidth / viii, pixelWidth / four, token), token);         third = Task.Manufacturing plant.StartNew(() => generateGraphData(data, pixelWidth / four, pixelWidth * iii / 8, token), token);         quaternary = Task.Mill.StartNew(() => generateGraphData(information, pixelWidth * iii / 8, pixelWidth / 2, token), token);         Task.WaitAll(showtime, second, tertiary, quaternary);                              plotButton.Dispatcher.Invoke(new Action(() =>                              {                              status.Content = "Tasks Completed";                              }), DispatcherPriority.ApplicationIdle);                              }                              take hold of (AggregateException ae)                              {                              ae.Handle(handleException);                              }              String bulletin = String.Format("Status of tasks is {0}, {ane}, {2}, {3}",           showtime.Status, 2nd.Status, third.Condition, fourth.Status);       MessageBox.Show(message);        return information; }
  7. In the generateDataForGraph method, replace the if statement that examines the IsCancellationProperty of the CancellationToken object with code that calls the ThrowIfCancellationRequested method, every bit shown here in bold:

    individual void generateDataForGraph(byte[] data, int partitionStart, int partitionEnd, CancellationToken token) {    ...     for (int 10 = partitionStart; x < partitionEnd; ten++);     {         ...         for (double i = -p; I < p; i += 3)         {                              token.ThrowIfCancellationRequested();              ...         }      }      ... }
  8. On the Debug menu, click Kickoff Without Debugging.

  9. In the Graph Demo window, click Plot Graph and verify that the status of every task is reported as RanToCompletion, the graph is generated, and the status label displays the message "Tasks Completed".

  10. Click Plot Graph over again, and so quickly click Abolish. If you are quick, the status of one or more tasks should be reported as Canceled, the condition characterization should display the text "Tasks Canceled", and the graph should exist displayed with holes. If you are not quick enough, repeat this step to try again!

  11. Close the Graph Demo window, and render to Visual Studio.

Using Continuations with Canceled and Faulted Tasks

If you need to perform additional work when a task is canceled or raises an unhandled exception, think that you can utilise the ContinueWith method with the advisable TaskContinuationOptions value. For instance, the following code creates a job that runs the method doWork. If the task is canceled, the ContinueWith method specifies that another task should be created and run the method doCancellationWork. This method can perform some simple logging or tidying up. If the task is not canceled, the continuation does non run.

Job chore = new Task(doWork); job.ContinueWith(doCancellationWork, TaskContinuationOptions.OnlyOnCanceled); chore.First(); ... private void doWork() {     // The task runs this code when information technology is started     ... } ... private void doCancellationWork(Task task) {     // The task runs this lawmaking when doWork completes     ... }

Similarly, you lot can specify the value TaskContinuationOptions.OnlyOnFaulted to specify a continuation that runs if the original method run by the task raises an unhandled exception.

In this chapter, you lot learned why it is important to write applications that can scale beyond multiple processors and processor cores. You saw how to employ the Task Parallel Library to run operations in parallel, and how to synchronize concurrent operations and wait for them to consummate. You learned how to use the Parallel class to parallelize some common programming constructs, and you lot also saw when it is inappropriate to parallelize code. Y'all used tasks and threads together in a graphical user interface to improve responsiveness and throughput, and you saw how to abolish tasks in a clean and controlled manner.

  • If y'all want to continue to the next chapter

    Keep Visual Studio 2010 running, and turn to Affiliate 28.

  • If you want to get out Visual Studio 2010 now

    On the File menu, click Exit. If you lot see a Salvage dialog box, click Yes and salve the project.

richardsongolould.blogspot.com

Source: https://www.microsoftpressstore.com/articles/article.aspx?p=2228455&seqNum=4

0 Response to "C Cancel Task and Start It Up Again"

Publicar un comentario

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel