Learning TPL (III)

Source: Internet
Author: User

Previous episode Review

The preceding section discusses the thread security of TPL and the performance of TPL. Aside from this, we will discuss a very powerful new feature of TPL-asynchronous revocation.

Application scenarios

Remember the thread pool, and use the thread pool to output a 0-9 number per second in sequence:

   1:              ThreadPool.QueueUserWorkItem(_ =>
   2:                  {
   3:                      for (int i = 0; i < 10; i++)
   4:                      {
   5:                          Console.WriteLine(i);
   6:                          Thread.Sleep(1000);
   7:                      }
   8:                  });

However, which of the following functions can be canceled?

To cancel the cancellation, we have to write the code as follows:

   1:              bool isCancelled = false;
   2:              ThreadPool.QueueUserWorkItem(_ =>
   3:                  {
   4:                      for (int i = 0; i < 10; i++)
   5:                      {
   6:                          Console.WriteLine(i);
   7:                          if (isCancelled)
   8:                              break;
   9:                          Thread.Sleep(1000);
  10:                      }
  11:                  });

Think it's good? No, it is not good at all. isCancelled will be accessed by multiple threads. Therefore, to ensure that the CLR will not optimize isCancelled, you need to add the volatile keyword and give the CLR an Hint.

Withdrawal of the new version

In 4.0, the same operation can be implemented as follows:

   1:              CancellationTokenSource cts = new CancellationTokenSource();
   2:              ThreadPool.QueueUserWorkItem(obj =>
   3:                  {
   4:                      CancellationToken token = (CancellationToken)obj;
   5:                      for (int i = 0; i < 10; i++)
   6:                      {
   7:                          Console.WriteLine(i);
   8:                          if (token.IsCancellationRequested)
   9:                              break;
  10:                          Thread.Sleep(1000);
  11:                      }
  12:                  }, cts.Token);
  13:              cts.Cancel();

It seems that there is no difference, but it is to change a bool to a class.

At least now it seems like this. If the withdrawal work was not that simple, and a Rollback action was required, what would happen?

At this time, we will find that the original method is very difficult to solve (at least let people make mistakes), and the use of the new revocation mechanism can be written as follows:

   1:              CancellationTokenSource cts = new CancellationTokenSource();
   2:              ThreadPool.QueueUserWorkItem(obj =>
   3:                  {
   4:                      CancellationToken token = (CancellationToken)obj;
   5:                      for (int i = 0; i < 10; i++)
   6:                      {
   7:                          Console.WriteLine(i);
   8:                          if (token.IsCancellationRequested)
   9:                          {
  10:                              token.Register(() =>
  11:                                  {
  12:                                      for (int j = i; j >= 0; j--)
  13:                                      {
14: Console. WriteLine ("undo" + j );
  15:                                      }
  16:                                  });
  17:                              break;
  18:                          }
  19:                          Thread.Sleep(100);
  20:                      }
  21:                  }, cts.Token);
  22:              Thread.Sleep(500);
  23:              cts.Cancel();
  24:              Thread.Sleep(100);

The execution result is:

0
1
2
3
4
5

Revoke 5

Revoke 4
Revoke 3
Revoke 2
Revoke 1
Undo 0
Press any key to continue...

Using the CancellationToken. Register Method, You can execute an additional undo code when Undo is performed. Therefore, the implementation is quite simple when you implement this undo.

ThreadPool can still be used like this, so do you have to think about the combination of TPL and such revocation?

Unrecoverable concurrent tasks

Let's start with the simplest task. For example, if there is a task with 10 steps (aside from concurrency, we only want to execute it in sequence), each step may fail, and then we need to roll back, of course, ideally, all three steps are successful, and then submitted. Using the Task class, you can implement a staged Task as follows:

   1:              using (var cancellation = new CancellationTokenSource())
   2:              using (var mres = new ManualResetEventSlim(false))
   3:              {
4: // Add a rollbacked task
   5:                  cancellation.Token.Register(() =>
   6:                  {
7: Console. WriteLine ("Installation failed and rollback successful! ");
   8:                      mres.Set();
   9:                  });
  10:                  Task[] tasks = new Task[10];
11: // Add a Welcome task
  12:                  var lastTask = Task.Factory.StartNew(() =>
  13:                  {
14: Console. WriteLine ("welcome to the simulated Installation Wizard! ");
  15:                  });
  16:                  for (int i = 0; i < 10; i++)
  17:                  {
18: // if you know the c # closure syntax, you must know the role of this sentence.
  19:                      int j = i;
  20:                      tasks[j] = lastTask.ContinueWith(_ =>
  21:                      {
22: // MessageBox is used directly, so I am lazy.
23: if (MessageBox. Show ("whether execution is successful" + j, "Test", MessageBoxButtons. YesNo) = DialogResult. Yes)
  24:                          {
25: Console. WriteLine ("execution step" + j + "is successful. ");
26: // Add the corresponding Rollback task for each successful task execution
  27:                              cancellation.Token.Register(() =>
  28:                              {
29: Console. WriteLine ("rollback step" + j + ". ");
  30:                              });
  31:                          }
  32:                          else
  33:                          {
  34:                              cancellation.Cancel();
  35:                          }
  36:                      }, cancellation.Token);
  37:                      lastTask = tasks[j];
  38:                  }
39: // Add a completed task
  40:                  var completedTask = lastTask.ContinueWith(_ =>
  41:                  {
42: Console. WriteLine ("installation successful! ");
  43:                      mres.Set();
  44:                  }, cancellation.Token);
  45:                  mres.Wait();
  46:              }

Run the following command to see if all steps are correct:

Welcome to the simulated Installation Wizard!
Step 0 is successfully executed.
Step 1 is successfully executed.
Step 2 is successfully executed.
Step 3 is successfully executed.
Step 4 is successfully executed.
Step 5 is successfully executed.
Step 6 is successfully executed.
Step 7 is successfully executed.
Step 8 is successfully executed.
Step 9 is successfully executed.
Installed successfully!
Press any key to continue...

The result of STEP 0-5 is Yes and Step 1 is No:

Welcome to the simulated Installation Wizard!
Step 0 is successfully executed.
Step 1 is successfully executed.
Step 2 is successfully executed.
Step 3 is successfully executed.
Step 4 is successfully executed.
Step 5 is successfully executed.
Step 5.
Step 4.
Step 3.
Rollback step 2.
Rollback step 1.
Rollback STEP 0.
Installation failed and rolled back successfully!
Press any key to continue...

Is it a bit like that. However, is it a waste of time to use a Task to avoid multiple concurrent tasks? Well, let's change the 10 tasks to parallel execution. The 10 tasks are executed in parallel. If all the tasks are completed, the operation is successful, and any one fails, you need to roll back the previous operation. It seems a little difficult at first glance, but you can easily modify the previous Code:

   1:              using (var cancellation = new CancellationTokenSource())
   2:              using (var mres = new ManualResetEventSlim(false))
   3:              {
4: // Add a rollbacked task
   5:                  cancellation.Token.Register(() =>
   6:                  {
7: Console. WriteLine ("Installation failed and rollback successful! ");
   8:                      mres.Set();
   9:                  });
  10:                  Task[] tasks = new Task[10];
11: // Add a Welcome task
  12:                  var welcomeTask = Task.Factory.StartNew(() =>
  13:                  {
14: Console. WriteLine ("welcome to the simulated Installation Wizard! ");
  15:                  });
  16:                  for (int i = 0; i < 10; i++)
  17:                  {
18: // if you know the c # closure syntax, you must know the role of this sentence.
  19:                      int j = i;
  20:                      tasks[j] = welcomeTask.ContinueWith(_ =>
  21:                      {
22: // MessageBox is used directly, so I am lazy.
23: if (MessageBox. Show ("whether execution is successful" + j, "Test", MessageBoxButtons. YesNo) = DialogResult. Yes)
  24:                          {
25: Console. WriteLine ("execution step" + j + "is successful. ");
26: // Add the corresponding Rollback task for each successful task execution
  27:                              cancellation.Token.Register(() =>
  28:                              {
29: Console. WriteLine ("rollback step" + j + ". ");
  30:                              });
  31:                          }
  32:                          else
  33:                          {
  34:                              cancellation.Cancel();
  35:                          }
  36:                      }, cancellation.Token);
  37:                  }
38: // Add a congratulation task
  39:                  var congratulationTask = Task.Factory.ContinueWhenAll(tasks, _ =>
  40:                  {
41: Console. WriteLine ("installation successful! ");
  42:                      mres.Set();
  43:                  }, cancellation.Token);
  44:                  mres.Wait();
  45:              }

Let's see what you have modified:

  • Delete lastTask = tasks [j];. Therefore, all the pre-tasks in the task are welcome tasks.
  • Change the prerequisite of the original congratulation task to all tasks in task

In this way, tasks in the task are automatically executed in parallel:

Select "yes", that is:

If you select no in STEP 0, you can obtain the following results:

Have you found that the rollback steps are in a different sequence from the steps that are successfully executed?

Yes, because the execution of these steps is concurrent, so the revocation is also concurrent, so the result is out of order. It can be compared with the previous execution method in pure order. The revocation of sequential execution is sequential, and the revocation of parallel execution is parallel. Is it amazing?

Thoughts

Do you still feel something missing?

What if the cancellation fails?

If a task has a subtask?

 

 

 

Indeed, there are still many questions worth thinking about in this regard. However, since there is not much time to contact TPL, we still need to learn more about this.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.