C#如何优雅地取消一个流程(非Thread.Abort方法)

摘要:
1、 线程的缺点。Abort()是我们使用线程。Abort()停止线程包装进程。privatepoolLongTimeMethod(){lock(asyncObject){if(this.isCancel){//如果取消该怎么办}线程。睡眠(10*1000);

一. Thread.Abort() 的缺点

我们使用 Thread.Abort() 来中止一个包裹着某个流程的线程,虽然 C# 并不会像 Thread.Suspend() 提示过时。但是在使用 Thread.Abort() 的时候,确实存在很多的问题:

1. 该方式中止线程是通过在线程执行的时候抛出 ThreadAbortException 异常来实现的。这边抛出的 ThreadAbortException 异常,不一定可以被局部的程序异常处理程序准确地捕获,而会被抛出在全局,需要通过 AppDomain.CurrentDomain.UnhandledException 来进行捕获处理;

注:关于 AppDomain.CurrentDomain.UnhandledException 的使用,请看下面的文章:

《关于C# 全局异常捕获》

2. 不能确定线程中止的时间和在中止之前所执行到的位置。总之,它取消线程是一个队列的处理方式,所以处理不是被立即响应的,而是要等一会,这就导致了程序的中止时间和在中止之前执行到了哪里都是未知的;

二. 代替方法

  • 那么我们有没有什么方法来代替这种 Thread.Abort() 的方式呢?

答:有。分析:中止一个流程,我们一定要向这个流程发送一个中止的信号,当这个流程获得这个信号的时候,立刻中断之后的操作并返回。因此,我们可以分析出来,这个流程在工作的时候,起码要有两个角色

  1. 一个是用来处理流程,也就是业务;
  2. 一个是用来处监视取消信号,并终止流程;

综上,我们可以考虑使用委托的异步调用来实现,如果要取消就使用轮序信号来实现。

三. 代码

  • 实现委托异步调用的类 LongTime.cs 代码:
public class LongTime
{
    public bool isComplete = false;

    public bool isCancel = false;

    public bool isSuccess = false;

    private object asynObject = new object();

    private bool LongTimeMethod()
    {
        lock (asynObject)
        {
            if(this.isCancel)
            {
                //如果取消了要做什么操作
            }

            Thread.Sleep(10 * 1000);
            return false;
        }
    }

    public void Test()
    {
        Console.WriteLine("开始...");

        //异步执行体
        AsyncCallback callback = (r) =>
        {
            this.isComplete = r.IsCompleted;
        };

        IAsyncResult result = ((Action)(() =>
        {
            this.isSuccess = LongTimeMethod();
        }))
        .BeginInvoke(callback, null);

        //执行表征体
        while (!isCancel && !result.IsCompleted)
        {
            Thread.Sleep(50);
        }

        Console.WriteLine(this.isCancel ? "取消" : "完成");
    }

    /*
     * ❤重要❤
     * 会处在一个异步的线程中,如果这里面的内容与LongTimeMethod方法中的内容有冲突
     * 那么会导致线程先后的次序的问题
     * 所以这边要加上lock
     */
    public void Cancel()
    {
        lock (asynObject)
        {
            this.isCancel = true;

            //如果执行完成了业务逻辑之后的补救操作
        }
    }
}
  • 上端调用测试代码:
LongTime longTime = new LongTime();

//随机取消
Thread thread = new Thread(() =>
    {
        Thread.Sleep(2000);
        if ((new Random()).Next() % 2 == 0)
        {
            longTime.Cancel();
        }
    });
thread.Start();

//开始测试
longTime.Test();

Console.ReadKey();

四. 要点分析

  • 我们需要注意什么呢?

答:要注意的是在发送消息之后,更新、判断取消状态的线程异步问题。所以代码要上锁,如下:

- 业务逻辑:

private bool LongTimeMethod()
{
    lock (asynObject)
    {
     if(this.isCancel)
        {
            //如果取消了要做什么操作
        }

        Thread.Sleep(10 * 1000);
        return false;
    }
}

- 取消操作:

public void Cancel()
{
    lock (asynObject)
    {
        this.isCancel = true;

        //如果执行完成了业务逻辑之后的补救操作
    }
}

由于我们无法知道,是 Cancel() 方法先修改 isCancel 标志位的值,还是 LongTimeMethod() 业务方法先判断 isCancel 为 false 并执行业务逻辑代码。所以程序在为后者的情况下,要在后执行的 Cancel() 方法中添加用于直接完成了业务逻辑的补救操作。

  • 示例程序,这是一个打开一个窗体程序,并取消前一个窗体之后再次打开一个窗体程序的代码示例

- 窗体程序就是打开一个窗体,名字叫 " DemoForm.exe " 的程序

- 实现异步委托调用的类,也就是业务逻辑类 LongTimeEx.cs,代码如下:

public class LongTimeEx
{
    public static bool isComplete = true;

    public static bool isCancel = true;

    public static bool isSuccess = false;

    private  static object asynObject = new object();

    private bool LongTimeMethod()
    {
        lock (asynObject)
        {
            if (isCancel)
            {
                return false;
            }

            using (Process process = new Process())
            {
                process.StartInfo.FileName = "DemoForm.exe";
                process.Start();
            }

            return true;
        }
    }

    public void Test()
    {
        Console.WriteLine("开始...");

        //判断一下上一个是不是已经结束
        while (!LongTimeEx.isComplete || !LongTimeEx.isCancel)
        {
            Thread.Sleep(50);
        }

        //由于修改为了静态的变量 
        //导致这边每次都要重新刷新
        isComplete = false;
        isCancel = false;

        //异步执行体
        AsyncCallback callback = (r) =>
        {
            isComplete = r.IsCompleted;
        };

        IAsyncResult result = ((Action)(() =>
        {
            isSuccess = LongTimeMethod();
        }))
        .BeginInvoke(callback, null);

        //执行表征体
        while (!isCancel && !result.IsCompleted)
        {
            Thread.Sleep(50);
        }
    }

    public void Cancel()
    {
        lock(asynObject)
        {
            isCancel = true;

            //取消一下启动的进程
            Process[] process = Process.GetProcessesByName("DemoForm");
            if(process.Count()>0)
            {
                foreach(Process p in process)
                {
                    p.Kill();
                }
            }
        }
    }
}

- 上端的调用代码:

LongTimeEx longTimeEx = new LongTimeEx();
longTimeEx.Test();
longTimeEx.Cancel();
longTimeEx.Test();

Console.ReadKey();

五. 示例代码下载

下载地址

免责声明:文章转载自《C#如何优雅地取消一个流程(非Thread.Abort方法)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【转】android电池(四):电池 电量计(MAX17040)驱动分析篇WPF学习问题汇集:下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Windows下Critical Section、Event、Mutex、Semaphores区别

临界区(Critical Section) 保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。 临界区包...

关于对JMM(java内存模型)的个人理解

java内存模型是一种虚拟机规范,它定义了Java内存模型,用于屏蔽各种不同硬件和操作系统访问内存差异,以实现让java程序在各种平台下都能达到一致的并发效果.JMM规范了java虚拟机与计算机内存是如何协同工作的,规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量. 关于主内存与工作内存之间的具体交互协...

【转】DELPHI 线程类

原文地址:http://yyimen.blog.163.com/blog/static/179784047201211811178223/ Delphi中有一个线程类TThread是用来实现多线程编程的,这个绝大多数Delphi书藉都有说到,但基本上都是对TThread类的几个成员作一简单介绍,再说明一下Execute的实现和Synchronize的用法就...

VC++中隐藏代码

1、引言       在VS编辑器中可以对类中的方法、注释等内容进行隐藏,单击左侧的‘-’号即可完成隐藏,隐藏后变为‘+’,单击‘+’号可以将隐藏的代码展开。 2、隐藏任意代码       如果想在编辑器中隐藏任意代码段,可以通过#pragma region <名称1>和#pragmaendregion <名称2>包含要隐藏的代码即...

Java多线程6:synchronized锁定类方法、volatile关键字及其他

同步静态方法 synchronized还可以应用在静态方法上,如果这么写,则代表的是对当前.java文件对应的Class类加锁。看一下例子,注意一下printC()并不是一个静态方法: public classThreadDomain25 { public synchronized static voidprintA() {...

框架模块设计经验总结

转自:http://www.cnblogs.com/zgynhqf/archive/2011/07/15/2107593.html 这是原创,尊重原创、、、、、、、、、、、、     框架模块设计经验总结      三个月没写日志了,比较懒散……下半年准备做OEA 的 B/S 版本,比较复杂,需要从架构设计开始认真入手。正好今天到了部门反思的时间,今天...