zoukankan      html  css  js  c++  java
  • 多线程之旅(9)_如何安全的取消正在执行的线程——附C#源码

    参考网址: https://blog.csdn.net/yangwohenmai1/article/details/90404497

    当线程能流畅安全的自动运行后,我们就要考虑一些更风骚的操作,就是如何在线程运行的过程中对线程进行干预。

    用Above销毁线程首先过于简单粗暴,强行停止往往会抛出一些未知的错误,而且也丧失了对线程控制的能力。

    此时,我们可以通过CancellationTokenSource来优雅的取消一个正在执行的线程。何谓优雅?优雅就是我们在发出指令停止一个线程的时候,线程会判断此时是否可以停止;如果可以停止的话;线程会对当前任务进行一些善后处理;最后,在停止后线程可以返回给我们一些信息告诉我们线程是否已经停掉。

    那么,我们怎么去随心所欲的取消这个线程的运行?

    Github源码地址:https://github.com/yangwohenmai/TEST/tree/master/TaskCancellationToken

    一.通过CancellationTokenSource.Cancel()取消单个任务
    1.在用Task.Factory.StartNew启动一个线程时,将CancellationTokenSource.Token参数一起传进去。

    2.同时在Task内部设置一个循环去判断CancellationTokenSource.IsCancellationRequested的状态,根据IsCancellationRequested不同的状态编写不同的执行逻辑。

    3.当想要取消这个正在执行的线程时,在外部调用CancellationTokenSource对象的Cancel()方法,就会修改IsCancellationRequested的状态。

    4.线程内部一旦读取到IsCancellationRequested状态为true,则代表线程取消的信号被触发,线程此时停止继续执行。

    代码如下:

    #region 取消单个任务
    /// <summary>
    ///
    /// </summary>
    public static void CancellationTokenTest()
    {
    //通知 System.Threading.CancellationToken,告知其应被取消。
    CancellationTokenSource TokenSource = new CancellationTokenSource();

    var task = Task.Factory.StartNew(() => DoWork(TokenSource.Token), TokenSource.Token);

    //在这里还可以给token注册了一个方法,输出一条信息 用户输入0后,执行tokenSource.Cancel方法取消任务。这个取消任务执行后,会执行这个代码.
    TokenSource.Token.Register(() => { Console.WriteLine("取消");Console.WriteLine("我点击了取消");return; });

    //等待用户输入
    var input = Console.ReadLine();

    if (Convert.ToInt32(input) == 0)
    {
    //如果输入了0,则取消这个任务;
    //一旦cancel被调用。task将会抛出OperationCanceledException来中断此任务的执行,最后将当前task的Status的IsCanceled属性设为true
    TokenSource.Cancel();
    }

    Thread.Sleep(10000);
    Console.WriteLine("任务是否完成:" + task.IsCompleted);
    Console.WriteLine("任务是否成功:" + task.IsCompletedSuccessfully);
    //当线程是因为异常而取消时,IsCanceled字段为true,正常取消时显示为false
    Console.WriteLine("任务是否是被手动取消:" + task.IsCanceled);
    Console.ReadLine();
    }


    /// <summary>
    /// 使用IsCancellationRequested终止一个线程
    /// </summary>
    /// <param name="Token"></param>
    public static void DoWork(CancellationToken Token)
    {
    int count = 0;
    for (var i = 1; i < 10; i++)
    {
    Console.WriteLine("i:" + i);
    Thread.Sleep(1000);
    //判断是否取消任务,取消为true;不取消为false
    if (Token.IsCancellationRequested)
    {
    count++;
    Console.WriteLine("取消任务成功!" + count);

    if (count == 5)
    {
    return;
    }
    }
    }
    }
    #endregion
    那这和我们自己在外部设置一个变量,通过修改变量来控制线程线程有什么区别呢?其实本质没区别,就是可读性更好,有现成的为何要自己写,而且也不用担心自己的外部变量被不小心修改。

    二.通过CancellationTokenSource.CreateLinkedTokenSource()取消任意一个任务,则所有任务都取消
    如果有一堆线程在同时执行,这一堆线程之间是一个事务类型的原子操作,一旦停止的话要一定要同时停止,但我不想把所有线程都单独进行一遍Cancel()操作,那么你就用CancellationTokenSource.CreateLinkedTokenSource这个集合。

    这个集合可以将一批线程一起放进集合里,你只要停掉这一批线程中的任意一个,那么这一批线程就会全部停止,听起来是不是很好用?

    代码如下:

    //声明CancellationTokenSource对象
    static CancellationTokenSource c1 = new CancellationTokenSource();
    static CancellationTokenSource c2 = new CancellationTokenSource();
    static CancellationTokenSource c3 = new CancellationTokenSource();

    //使用多个CancellationTokenSource进行复合管理
    static CancellationTokenSource compositeCancel = CancellationTokenSource.CreateLinkedTokenSource(c1.Token, c2.Token, c3.Token);
    #region 取消任意一个任务,所有的任务都取消
    /// <summary>
    /// 取消任意一个任务,则所有任务都取消
    /// </summary>
    public static void CancellationTokenAllTest()
    {
    var task = Task.Factory.StartNew(() =>
    {
    for (var i = 1; i < 1000; i++)
    {
    Console.WriteLine("Hello World!");
    Thread.Sleep(1000);
    //判断是否取消任务,取消为true;不取消为false
    if (compositeCancel.IsCancellationRequested)
    {
    Console.Write("取消任务成功!");
    return;
    }
    }
    }, compositeCancel.Token);


    //等待用户输入
    var input = Console.ReadLine();

    //如果输入了0,则取消c1这个任务,c1取消后,符合管理compositeCancel的状态都取消
    if (Convert.ToInt32(input) == 0)
    {
    //任意一个 CancellationTokenSource 取消任务,那么所有任务都会被取消
    c1.Cancel();
    }
    }
    #endregion
    3.Register
    在CancellationTokenSource.Token之后还有一个Register()方法,这是一个回调方法,当CancellationToken被取消后,就会自动调用Register()内部定义的方法。

    这里我们可以进行一些线程停止的善后处理

    TokenSource.Token.Register(() =>
    {
    Console.WriteLine("取消");
    Console.WriteLine("我点击了取消");
    return;
    });

    ————————————————
    版权声明:本文为CSDN博主「日拱一两卒」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/yangwohenmai1/article/details/90404497

  • 相关阅读:
    如何不传入对象就获得某对象的方法---ThreadLocal类
    Linux系统主目录被更改,怎么修改回去?
    tree命令的安装
    Linux命令学习man
    当重载函数的参数是Object和Object数组的时候会发生什么情况!!!
    Linux学习(二)之内核、系统调用、库
    使用puttygen转换OpenSSH SSH2私钥为RSA PEM格式
    docker-compose使用详解
    svn迁移到gitlab
    linux快速启动http端口
  • 原文地址:https://www.cnblogs.com/bruce1992/p/15189061.html
Copyright © 2011-2022 走看看