【C#】线程协作式取消

Microsoft .Net Framework 提供了一个标准的取消操作的模式。这个模式是协作式的,意味着你想取消的操作必须显示地支持取消。

CLR为我们提供了两个类:

  System.Threading.CancellationTokenSource

  System.Threading.CancellationToken

CancellationToken实例是一个轻量级的值类型,因为它包含单个私有字段:CancellationTokenSource的一个引用。在一个计算限制操作的循环中,可以定时调用CancellationToken的IsCancellationRequested属性,了解循环是否应该提前终止,进而终止计算机限制的操作。

   我附上我常用代码:

CancellationTokenSource cancel=new CancellationTokenSource();

    Task.Factory.StartNew(() =>
        {
            while (!cancel.IsCancellationRequested)
            {
                 //do something
            }
        }, cancel.Token);// cancel.Token->CancellationToken
  
     //取消操作
     cancel.Cancel();
     cancel.Dispose();
     cancel=null; 

如果用到线程池,也可以使用这个类

  public void main()
  {
         var cancel=new CancellationTokenSource();
         ThreadPool.QueueUserWorkItem(_ =>
            {
                while (!cancel.IsCancellationRequested)
                {
                    //do something
                }
            });

            Thread.Sleep(10 * 1000);//为了让循环多做些时间
            cancel.Cancel();
            cancel.Dispose();
  }

这个类还有一个很好的地方,可以在自定义回调函数,在调用Cancel方法的时候使用。这个我们需要用到CancellationToken里面的Register方法(也就是new CancellationTokenSource().Token.Register(()=>{})),并且可以给一个Token多次注册,按照倒序执行。

 

 var cancel = new CancellationTokenSource();
            cancel.Token.Register(() =>
            {
                MessageBox.Show("Register3");
            });
            cancel.Token.Register(() =>
            {
                MessageBox.Show("Register");
            });
            cancel.Token.Register(() =>
            {
                MessageBox.Show("Register1");
            });
            cancel.Token.Register(() =>
            {
                MessageBox.Show("Register2");
            });

 弹出顺序:Register2Register1,Register,Register3

如果想注销注册的回调函数,需要用到CancellationTokenRegistration(这个在调用Register的时候就会返回),附代码:

        CancellationTokenRegistration registration = cancel.Token.Register(() =>
             {
                 MessageBox.Show("Register2");
             });
            registration.Dispose();//这里可以取消    

取消以后再跑,就只会弹出另外三个,还是倒序。

最后,可通过链接另一组CancellationTokenSource来新建一个CancellationTokenSource对象,任何链接的CancellationTokenSource被取消,这个新的CancellationTokenSource对象就会自动被取消,附代码:

  public static void Go()
        {
            var cts1 = new CancellationTokenSource();
            cts1.Token.Register(() => Console.WriteLine("cts1 canceled"));

            var cts2 = new CancellationTokenSource();
            cts2.Token.Register(() => Console.WriteLine("cts2 canceled"));

            var linkedcts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);
            linkedcts.Token.Register(() => Console.WriteLine("linkedcts canceled"));

            cts2.Cancel();

            Console.WriteLine("cts1:{0}  cts2:{1}  linkedcts:{2}",
                cts1.IsCancellationRequested, cts2.IsCancellationRequested, linkedcts.IsCancellationRequested);
        }

由于cts2对象被取消了,所以linkedcts自动被取消,这里CancellationTokenSource.CreateLinkedTokenSource 有一个重载是params CancellationToken[], 理论上说,无论加多少个CancellationToken对象都是可以的。

posted @ 2014-09-17 16:06  Eric Guo  阅读(2293)  评论(3编辑  收藏  举报