• C# Tip 如何优雅的控制线程状态


    先问读者一个问题,如果想控制一个线程(Thread)挂起,继续,结束你会优先选择如何处理?
    从面试的经验来看,多数的面试者都会说Thread不是提供了Suspend,Resume,Abort等方法吗,直接调用就好了,简单快捷.但很多情况下简单就是美只是一个谎言而已.
    Suspend和Resume方法已经被MS标注过时(Obsolete),不建议用户使用,MS同时推荐通过同步控制来管理线程状态.
    再问读者一个问题,如果控制一个线程池中的线程的挂起,继续,结束你会优先选择如何处理?
    也许有人会说,既然没法用API了,那我定义一些标志量,来做状态控制不就可以了吗?
    确实,说起来很简单.那么变量的值需不需要考虑由于多线程问题导致的读写问题呢,你会怎么处理?
    如果不用标志量,可以用同步事件来做,会不会更优雅一些呢?
    让我把传统的方式以及使用同步方式做的控制都写一遍,让读者来做取舍吧.
    代码如下,注释也比较全,就不再累述细节.
    class Program
    {
    static void Main(string[] args)
    {
    UseThreadAPI();
    UseSyncEvent();
    UseThreadPool();
    UseSyncEventThreadPool();
    Console.WriteLine(
    "All tests finished");
    Console.ReadKey();
    }
    /// <summary>
    /// 使用标准Thread API来控制线程状态,
    /// Suspend和Resume是过时的方法,MS不推荐使用
    /// MS推荐的方式就是后面要提到的使用Monitor,Event等做同步控制.
    /// </summary>
    static void UseThreadAPI()
    {
    Console.WriteLine(
    "----------Use thread API----------");
    Thread t
    = new Thread(
    new ThreadStart(() =>
    {
    while (true)
    {
    Console.WriteLine(
    "Now Date:{0}", DateTime.Now);
    Thread.Sleep(
    1000);
    }
    }
    ));
    t.Start();
    //暂停线程执行
    Console.ReadKey();
    t.Suspend();
    Console.WriteLine(
    "Thread suspended");
    //继续线程执行
    Console.ReadKey();
    t.Resume();
    Console.WriteLine(
    "Thread resumed");
    //结束线程
    Console.ReadKey();
    t.Abort();
    Console.WriteLine(
    "Thread aborted");
    Console.ReadKey();
    }
    /// <summary>
    /// 使用Event做同步控制
    /// 三个Event组合使用就可以产生同Suspend,Resuem,Abort相同的效果
    /// 而且你可以控制Abort的时机以及并作出适当的处理
    /// 而不是像Thread.Abort一样通过异常的方式结束线程
    /// </summary>
    static void UseSyncEvent()
    {
    Console.WriteLine(
    "----------Use sync event----------");
    AutoResetEvent evtPause
    = new AutoResetEvent(false);
    AutoResetEvent evtResume
    = new AutoResetEvent(false);
    AutoResetEvent evtStop
    = new AutoResetEvent(false);
    Thread t
    = new Thread(
    new ThreadStart(() =>
    {
    //WaitOne(1000),可产生Sleep(1000)相同的效果
    //如果eveStop被置位,则立即返回True,跳出循环
    //如果等待1000ms超时,则返回False,继续循环
    while (!evtStop.WaitOne(1000))
    {
    //WaitOne(0)可立即判断evtPase有没有被置位
    //如果置位,进入暂停状态,等待Resume被置位才会恢复线程执行
    if (evtPause.WaitOne(0))
    {
    //WaitOne()不带参数表示一直等待,直到被置位
    evtResume.WaitOne();
    }
    Console.WriteLine(
    "Now Date:{0}", DateTime.Now);
    }
    }
    ));
    t.Start();
    //暂停线程执行
    Console.ReadKey();
    evtPause.Set();
    Console.WriteLine(
    "Thread suspended");
    //继续线程执行
    Console.ReadKey();
    evtResume.Set();
    Console.WriteLine(
    "Thread resumed");
    //结束线程
    Console.ReadKey();
    evtStop.Set();
    Console.WriteLine(
    "Thread stopped");
    Console.ReadKey();
    }
    class ThreadStatusController
    {
    /// <summary>
    /// 声明为volatile可以避免用lock进行加锁同步
    /// 编译器自己会做优化
    /// 另外不能声明属性为volatile,因此只能作为成员变量放出.
    /// </summary>
    public volatile bool IsPauseRequired;
    public volatile bool IsStopRequired;
    }
    /// <summary>
    /// 对于放入线程池的进程,是无法通过Thread的API进行控制的
    /// 通常的做法是通过一些bool量做控制,这不是优雅的解决方案.
    /// </summary>
    static void UseThreadPool()
    {
    Console.WriteLine(
    "----------Use thread pool----------");
    var tsc
    = new ThreadStatusController();
    ThreadPool.QueueUserWorkItem(status
    =>
    {
    var ctrl
    = status as ThreadStatusController;
    //等待IsStopRequired标志量的值为True
    while (!ctrl.IsStopRequired)
    {
    //如果不是Pause请求,则执行
    if (!ctrl.IsPauseRequired)
    Console.WriteLine(
    "Now Date:{0}", DateTime.Now);
    Thread.Sleep(
    1000);
    }

    }, tsc);
    //暂停线程执行
    Console.ReadKey();
    tsc.IsPauseRequired
    = true;
    Console.WriteLine(
    "Thread suspended");
    //继续线程执行
    Console.ReadKey();
    tsc.IsPauseRequired
    = false;
    Console.WriteLine(
    "Thread resumed");
    //结束线程
    Console.ReadKey();
    tsc.IsStopRequired
    = true;
    Console.WriteLine(
    "Thread aborted");
    Console.ReadKey();
    }
    /// <summary>
    /// 声明三个同步事件分别对应三种同步状态
    /// </summary>
    class ThreadStatusEventController
    {
    public ThreadStatusEventController()
    {
    PauseEvent
    = new AutoResetEvent(false);
    ResumeEvent
    = new AutoResetEvent(false);
    StopEvent
    = new AutoResetEvent(false);
    }
    public AutoResetEvent PauseEvent { get; set; }
    public AutoResetEvent ResumeEvent { get; set; }
    public AutoResetEvent StopEvent { get; set; }
    }
    static void UseSyncEventThreadPool()
    {
    Console.WriteLine(
    "----------Use sync event with thread pool----------");
    var tsc
    = new ThreadStatusEventController();
    ThreadPool.QueueUserWorkItem(status
    =>
    {
    var ctrl
    = status as ThreadStatusEventController;
    //控制代码跟采用Thread的方式类似,不累述
    while (!ctrl.StopEvent.WaitOne(1000))
    {
    if (ctrl.PauseEvent.WaitOne(0))
    {
    ctrl.ResumeEvent.WaitOne();
    }
    Console.WriteLine(
    "Now Date:{0}", DateTime.Now);
    }
    }, tsc);
    //暂停线程执行
    Console.ReadKey();
    tsc.PauseEvent.Set();
    Console.WriteLine(
    "Thread suspended");
    //继续线程执行
    Console.ReadKey();
    tsc.ResumeEvent.Set();
    Console.WriteLine(
    "Thread resumed");
    //结束线程
    Console.ReadKey();
    tsc.StopEvent.Set();
    Console.WriteLine(
    "Thread aborted");
    Console.ReadKey();
    }

    }
     
  • 相关阅读:
    (原创)分享一下最近搞的tiny210V2从nand启动支持(K9GAG08U0F).
    (转)uboot 与系统内核中 MTD分区的关系
    (转)mkimage制作linux内核映像 即uImage是怎么制作的
    ExtJs 一些属性备忘录
    CSS 元素选择器
    为什么匿名内部类参数必须为final类型
    Unbuntu 14.04 64位 搭建交叉编译环境
    eclipse调试运行
    Linux中cat命令的用法
    android之json
  • 原文地址:https://www.cnblogs.com/bloodish/p/1990025.html
Copyright © 2020-2023  润新知