如何取消.net后台线程的执行

介绍
在使用多线程模型进行编程时,经常遇到的问题之一是,当我们关闭前台的UI线程时,后台的辅助线程仍然处于活动状态,从而导致整个应用程序无法正常退出。这时我们需要一种较安全的方式来结束后台线程的运行,这样我们可以随时结束后台线程的运行,并且在线程结束时进行相应的资源清理工作(例如将内存数据写入硬盘)。.net框架提供了一些工具来实现该功能。

目录
IsBackground属性
Abort方法
轮循方式
取消阻塞的线程
IsBackgound属性
Thread类提供了IsBackground属性,当线程的IsBackground属性被设置为true时,表示此线程为后台工作线程。当一个应用程序结束时,它的所有后台线程会自动的被结束执行。如果你有一个后台线程侦听Socket连接,并且正在被阻塞,那么这时候通过设置线程的IsBackground属性为True,使它自动随应用程序的结束而结束是比较合适的。但在这种情况下,线程会静悄悄的结束,它不会引发任何异常,你的线程没有机会执行一些需要的清理代码。例如,内存中的数据可能会来不及写入磁盘,从而造成丢失数据。

Abort方法
可以调用Thread类的Abort方法来强制终制线程。上调用此方法时,线程上引发ThreadAbortException,并导至线程终结,通过捕获该异常,可以执行一些资源清理代码。但这种模式也有一些问题,主要是难以知道线程上的代码执行到什么地方,所有相应的资源清理代码也难以编写。总的来说这是一种比较粗暴的终止线程执行的方法,通常来说是不推荐使用的。

轮循方式
如果后台线程将执行一个很长的计算,那么可以将计算隔成若干小段,并经常检查是否需要取消线程。.NET框架提供了CancellationTokenSource类来作为线程取消的统一模式。例如:


代码如下:

public class Example
{
public static void Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
var thread = new Thread(ThreadWork);
thread.Start(cts.Token);
while (true)
{
if(Console.ReadKey().KeyChar == 'c')
{
Console.WriteLine("请求取消线程的执行");
cts.Cancel();
break;
}
}
Console.ReadLine();
}

private static void ThreadWork(object state)
{
CancellationToken cancellationToken = (CancellationToken)state;

while (true)
{
// 检查是否取消
if(cancellationToken.IsCancellationRequested)
{
Console.WriteLine("线程已经取消了");
Console.WriteLine("线程的资源已经清理完成。");
break;
}
// 模拟工作
Thread.SpinWait(500000);
Console.WriteLine("我还在工作。");
}
}
}

取消阻塞的线程
上面的示例中,后台线程会长时间进行计算,但更多的时候,线程会由于等待某个事件,从而进入阻塞状态。这个时候,实际上线程已经不再执行状态了,很明显,它没有机会去检查取消标志。 那么,该如何解决这个问题呢?CancellationToken的WaitHandle属性提供了解答。WaitHandle类有一个静态方法WaitAny,它可以同时等待多个事件,当多个事件中的任意一个有效时,线程都会从阻塞状态中返回。可以根据WaitAny方法的返回值来判断发生了什么事件,从而相应的执行代码。例子:


代码如下:

public class Example
{
private static int Value;

public static void Main()
{
var autoResetEvent = new AutoResetEvent(false);
var cts = new CancellationTokenSource();
var state = new { ValueAvailableEvent = autoResetEvent, CancellationToken = cts.Token };
var threadConsumer = new Thread(ConsumerThreadWork);
var threadProducter = new Thread(ProducterThreadWork);

threadConsumer.Start(state);
threadProducter.Start(state);

while (true)
{
if (Console.ReadKey().KeyChar == 'c')
{
Console.WriteLine("请求取消线程的执行");
cts.Cancel();
break;
}
}
Console.ReadLine();

}
public static void ProducterThreadWork(dynamic state)
{
var valueAvailableEvent = (AutoResetEvent)state.ValueAvailableEvent;
var cancellationToken = (CancellationToken)state.CancellationToken;
var rand = new Random();
while (!cancellationToken.IsCancellationRequested)
{
Value = rand.Next();
Console.WriteLine("\r\n产生一个值{0}", Value);
valueAvailableEvent.Set();
Thread.Sleep(500);
}

Console.WriteLine("生产者线程被取消。");
}

public static void ConsumerThreadWork(dynamic state)
{
var valueAvailableEvent = (AutoResetEvent)state.ValueAvailableEvent;
var cancellationToken = (CancellationToken)state.CancellationToken;
var events = new[] { valueAvailableEvent, cancellationToken.WaitHandle };

while (true)
{
var eventIndex = WaitHandle.WaitAny(events);
// 处理数据
if (eventIndex == 0)
{
Console.WriteLine("处理值{0}。", Value);
}
// 处理取消事件
else if (eventIndex == 1)
{
Console.WriteLine("消费者线程被取消。");
break;
}
}
}
}

在上面的例子中,有三个线程,分别是UI线程,生产者线程和消费者线程。其中生产者线程每隔一秒产生一个有效数值,并将数据保存到Value字段中,而消费者线程等待值的产生,这个等待的过程是阻塞的。消费都线程通过WaitHandle.WaitAny方法来同时等待值有效事件或者取消事件,当任意一个事件有效时,线程都将继续,并且通过返回的值来判断发生的事件,并作相应的处理。

总结
多线程模型中的线程取消问题还是比较复杂的。Thread.IsBackground属性提供了在前台线程结束后自动结束线程的方法。Thread.Abort方法提供了一种“粗暴”的结束线程的方法。CancellationTokenSource类则是线程取消的标准模式,我们应当更多的使用这种模式。文章写的不多,基本是字数不够,代码来凑,大家伙将就的看看吧。

(0)

相关推荐

  • ASP.NET线程相关配置

    1.(maxWorkerThreads * CPU逻辑数量)-minFreeThreads 比如2个CPU默认配置maxWorkerThreads=100,minFreeThreads=176,则同时最大只能有24个工作线程.(这里不管  <system.net> <connectionManagement> <add address="*" maxconnection="8" /> </connectionManageme

  • .net中线程同步的典型场景和问题剖析

    在使用多线程进行编程时,有一些经典的线程同步问题,对于这些问题,.net提供了多种不同的类来解决.除了要考虑场景本身,一个重要的问题是,这些线程是否在同一个应用程序域中运行.如果线程都在同一应用程序域中运行,则可以使用一些所谓"轻量"级的同步类,否则要使用另一些类,而这些类都是对操作系统所提供的同步原语的包装,相对来说更消耗资源.我在这儿介绍一些典型的应用场景和相关的问题. 多线程争用独占资源 常常有一些资源线程独占的,如果有多个线程同时需要访问这要的资源,就形成了一个争用问题.这类资

  • 如何取消.net后台线程的执行

    介绍 在使用多线程模型进行编程时,经常遇到的问题之一是,当我们关闭前台的UI线程时,后台的辅助线程仍然处于活动状态,从而导致整个应用程序无法正常退出.这时我们需要一种较安全的方式来结束后台线程的运行,这样我们可以随时结束后台线程的运行,并且在线程结束时进行相应的资源清理工作(例如将内存数据写入硬盘)..net框架提供了一些工具来实现该功能. 目录 IsBackground属性 Abort方法 轮循方式 取消阻塞的线程 IsBackgound属性 Thread类提供了IsBackground属性,

  • C#中前台线程和后台线程的区别与联系

    前台线程和后台线程的区别和联系: 1.后台线程不会阻止进程的终止.属于某个进程的所有前台线程都终止后,该进程就会被终止.所有剩余的后台线程都会停止且不会完成. 2.可以在任何时候将前台线程修改为后台线程,方式是设置Thread.IsBackground 属性. 3.不管是前台线程还是后台线程,如果线程内出现了异常,都会导致进程的终止. 4.托管线程池中的线程都是后台线程,使用new Thread方式创建的线程默认都是前台线程. 下面做个小例子来证实一下,代码如下很简单: class Progra

  • Java中后台线程实例解析

    本文研究的主要是Java中后台线程的相关问题,具体介绍如下. 以前从来没有听说过,java中有后台线程这种东西.一般来说,JVM(JAVA虚拟机)中一般会包括俩种线程,分别是用户线程和后台线程.所谓后台线程(daemon)线程指的是:在程序运行的时候在后台提供的一种通用的服务的线程,并且这种线程并不属于程序中不可或缺的部分.因此,当所有的非后台线程结束的时候,也就是用户线程都结束的时候,程序也就终止了.同时,会杀死进程中的所有的后台线程.反过来说,只要有任何非后台线程还在运行,程序就不会结束.不

  • Java后台线程操作示例【守护线程】

    本文实例讲述了Java后台线程操作.分享给大家供大家参考,具体如下: 一 点睛 有一种线程,它是后面运行的,它的任务是为其他线程提供服务,这种线程被称为"后台"线程,又称为"守护线程"或"精灵线程".JVM的垃圾回收线程就是典型的后台线程. 后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡, 调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程,所有的前台线程都死亡时,后台线程随之死亡.当整个虚拟机中只

  • C#使用后台线程BackgroundWorker处理任务的总结

    在一些耗时的操作过程中,在长时间运行时可能会导致用户界面 (UI) 处于停止响应状态,用户在这操作期间无法进行其他的操作,为了不使UI层处于停止响应状态,我们倾向推荐用户使用BackgroundWorker来进行处理,这个后台的线程处理,可以很好的实现常规操作的同时,还可以及时通知UI,包括当前处理信息和进度等,这个BackgroundWorker的处理在百度里面也是有很多使用的介绍,本篇随笔主要是做一些自己的使用总结,希望也能给读者提供一个参考. 在使用BackgroundWorker的过程中

  • 区分c# 前台和后台线程

    Net的公用语言运行时(Common Language Runtime,CLR)能区分两种不同类型的线程:前台线程和后台线程.这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出:而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束. 这么说可能不知道有什么区别: 前台线程和后台线程的区别和联系: 1.后台线程不会阻止进程的终止.属于某个进程的所有前台线程都终止后,该进程就会被终止.所有剩余的后台线程都会停止且不会完成. 2.可以在

  • Java 使用线程池执行多个任务的示例

    在执行一系列带有IO操作(例如下载文件),且互不相关的异步任务时,采用多线程可以很极大的提高运行效率.线程池包含了一系列的线程,并且可以管理这些线程.例如:创建线程,销毁线程等.本文将介绍如何使用Java中的线程池执行任务. 1 任务类型 在使用线程池执行任务之前,我们弄清楚什么任务可以被线程池调用.按照任务是否有返回值可以将任务分为两种,分别是实现Runnable的任务类(无参数无返回值)和实现Callable接口的任务类(无参数有返回值).在打代码时根据需求选择对应的任务类型. 1.1 实现

  • .net让线程支持超时的方法实例和线程在执行结束后销毁的方法

    .net让线程支持超时 使用 CancellationTokenSource 复制代码 代码如下: private static void TimeoutTest1()        {            var cts = new CancellationTokenSource(); var thread = new Thread(() =>            {                Console.WriteLine(String.Format("线程{0}执行中&q

  • c#后台线程访问前台控件并显示信息示例

    复制代码 代码如下: //设置为后台线程 Thread th = new Thread(delegate() { append();});th.IsBackground = true;th.Start(); //在append方法里面需要调用前台控件 public void append(){ // ... 业务处理 this.Invoke(new flushMessage(showMessage), new object[] { row["Code"].ToString(), res

  • Android后台线程和UI线程通讯实例

    本节向你展示如何在任务中发送数据给UI线程里的对象,这个特性允许你在后台线程工作,完了在UI线程展示结果. 在UI线程定义一个Handler Handler是Android系统线程管理框架里的一部分.一个Handler对象接收消息,并且运行代码来处理消息.正常情况下,你为新线程创建Handler,但你也可以为已有的线程创建一个Handler.当你连接Handler到UI线程时,处理消息的代码会在UI线程上运行. 在创建线程池的类的构造器里实例化Handler对象,保存在全局变量里.用Handle

随机推荐