之前,我就开始关注Thread的知识。看过计算限制异步操作、I/O限制异步操作、Task、Parallel、APM、AsyncEnumerator…也做了相应的笔记。可是,具体Coding起来,还是会感到举步艰难。Why?复杂的知识让我复杂的想问题。上午,看了Jimmy Zhang如何在30岁前年薪超过30万,说到“我发现越是真理,就越是趋于简单”。也许,最好的是简单的,关键在于,你愿不愿意,能不能这样去做。

这里,不讲Thread的基本知识了。就陆续举出几个不同方法实现的例子,比较下,会发现简明才是美。(以下例子中,没有充分考虑到performance hit,请指正!)

 

协作式取消 VS 线程终止

协作式取消的介绍在Thread基础-计算限制的异步操作(CLR)介绍过了,这里不再做重复。直接Coding。

这里我们需要创建一个应用程序并在一个新线程中生成素数。在这个应用程序中有停止生成素数线程的选项。(盗用下DanielWise兄的例子哈)

界面如下:

Thread 编程:简明(1) - 协作式取消 VS 线程终止

协作式实现:

/*************************************
/* Copyright (c) 2012 mumuZ
 * 
 * Author:mumuZ
 * Blog:o  http://www.cnblogs.com/lnetmor/
 * Email:o lnetmor@gmail.com
 * 
 */
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace ThreadIsSoEasy_MonDontCare_
{
    public partial class Form1 : Form
    {
        private Thread primeNumberThread;
        public delegate void UpdateData(string returnVal);
        public delegate void UpdateButtonControl(string id, string isEnabled);
        private CancellationTokenSource cancellationTokenSource;
 
        public Form1()
        {
            InitializeComponent();
 
            cmdStart.Enabled = true;
            cmdStop.Enabled = false;
        }
 
        private void cmdStart_Click(object sender, EventArgs e)
        {
            lstPrime.Items.Clear();
 
            cancellationTokenSource = new CancellationTokenSource();
            cmdStop.Enabled = true;
            cmdStart.Enabled = false;
            ThreadPool.QueueUserWorkItem(o => GeneratePrimeNumbers(cancellationTokenSource.Token));
        }
 
        private void cmdStop_Click(object sender, EventArgs e)
        {
            cancellationTokenSource.Cancel();
            cmdStop.Enabled = false;
            cmdStart.Enabled = true;
        }

 

GeneratePrimeNumbers方法需要传入一个CancellationToken实例。具体实现如下:

public void GeneratePrimeNumbers(CancellationToken token)
{
    long lngCounter = 2;
    long lngNumber = 3;
    long lngDivideByCounter;
    bool blnIsPrime;
    long[] PrimeArray = new long[256];
    string[] args = new string[] { "2" };
    UpdateData UIDel = new UpdateData(UpdateUI);
    UpdateButtonControl UIControl = new UpdateButtonControl(UpdateButtonState);
 
    //We know that the first prime is 2.
    //Therefore, let's add it to the list and start from 3.
    PrimeArray[1] = 2;
    //lstPrime.Items.Add(2);
    this.Invoke(UIDel, args);
 
    while (lngCounter < 256)
    {
        blnIsPrime = true;
 
        if (token.IsCancellationRequested)
        {
            args[0] = "Cancelled!";
            this.Invoke(UIDel, args);
            break;
        }
 
        //Try dividing this number by any already found prime
        //Which is smaller then the root of this number.
        for (lngDivideByCounter = 1; PrimeArray[lngDivideByCounter]
            * PrimeArray[lngDivideByCounter] <= lngNumber;
            lngDivideByCounter++)
        {
            if (lngNumber % PrimeArray[lngDivideByCounter] == 0)
            {
                //This is not a prime number
                blnIsPrime = false;
                //Exit the loop break;
            }
        }
 
        //If this is a prime number then display it
        if (blnIsPrime)
        {
            //Guess we found a new prime.
            PrimeArray[lngCounter] = lngNumber;
            //Increase prime found count.
            lngCounter++;
            //lstPrime.Items.Add(lngNumber);
            args[0] = lngNumber.ToString();
            this.Invoke(UIDel, args);
            //Sleep 100 milliseconds.
            //This will simulate the time lag and we'll get time
            //to pause and resume the thread.
            Thread.Sleep(100);
        }
        lngNumber += 2;
    }
    this.Invoke(UIControl, new string[] { "cmdStart", "true" });
    this.Invoke(UIControl, new string[] { "cmdStop", "false" });
}

细心地朋友应该注意到GeneratePrimeNumbers方法内,我是用两个委托来修改UI的,相信大家都知道其中的道理,不多说了。下面就来看下由线程终止来实现该功能。

/*************************************
/* Copyright (c) 2012 mumuZ
 * 
 * Author:mumuZ
 * Blog:o  http://www.cnblogs.com/lnetmor/
 * Email:o lnetmor@gmail.com
 * 
 */
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace ThreadIsSoEasy_MonDontCare_
{
    public partial class ThreadAbortForm : Form
    {
        private Thread primeNumberThread;
        public delegate void UpdateData(string returnVal);
        public delegate void UpdateButtonControl(string id, string isEnabled);
 
        public ThreadAbortForm()
        {
            InitializeComponent();
            cmdStart.Enabled = true;
            cmdStop.Enabled = false;
        }
 
        private void cmdStart_Click(object sender, EventArgs e)
        {
            lstPrime.Items.Clear();
            primeNumberThread = new Thread(new ThreadStart(GeneratePrimeNumbers));
            primeNumberThread.Start();
            cmdStart.Enabled = false;
            cmdStop.Enabled = true;
        }
 
        private void cmdStop_Click(object sender, EventArgs e)
        {
            primeNumberThread.Abort();
            lstPrime.Items.Add("Cancelled!");
            cmdStart.Enabled = true;
            cmdStop.Enabled = false;
        }

现在,GeneratePrimeNumbers方法不需要有CancellationToken了。即,内部逻辑就简略了很多。

以上两个例子孰差孰优,仁者见仁,智者见智吧。笔者还是新苗于后者,这样我的代码就可以少CancellationTokenSource 和CancellationToken两个类,并且生成素数方法GeneratePrimeNumbers的逻辑也简明了。

But, 当停止一个操作的时候,有很多后续操作需要执行,你可能需要用到CancellationToken`s Register方法,这时候使用协作式取消来的简明了吧。

 

代码下载

 

下期预告:更改UI 同步上下文TaskSchedule VS 线程池的TaskSchedule

相关文章:

  • 2022-12-23
  • 2022-01-07
  • 2022-12-23
  • 2021-04-10
  • 2022-12-23
  • 2021-06-28
  • 2022-12-23
  • 2021-06-29
猜你喜欢
  • 2021-12-24
  • 2021-06-15
  • 2021-06-09
  • 2022-12-23
  • 2021-09-14
  • 2021-09-01
  • 2022-12-23
相关资源
相似解决方案