• 后台线程


      启动后台线程有很多种方式,下面是几种常用的方式,各个方法的优缺点尚未可知。

    1、System.Threading.Thread 

    public void TestThread()
    {
        Thread thread = new Thread(_ => BackgroundMethod("Test Thread"));
        thread.Start();
        thread.Join();
    }

      Thread类是创建并控制线程,设置其优先级并获取其状态最为常用的类。

      Thread.Start():启动线程的执行;
      Thread.Suspend():挂起线程,或者如果线程已挂起,则不起作用;
      Thread.Resume():继续已挂起的线程;
      Thread.Interrupt():中止处于 Wait或者Sleep或者Join 线程状态的线程;
      Thread.Join():阻塞调用线程,直到某个线程终止时为止;
      Thread.Sleep():将当前线程阻塞指定的毫秒数;
      Thread.Abort():以开始终止此线程的过程。如果线程已经在终止,则不能通过Thread.Start()来启动线程。

      通过调用Thread.Sleep,Thread.Suspend或者Thread.Join可以暂停/阻塞线程。调用Sleep()和Suspend()方法意味着线程将不再得到CPU时间。这两种暂停线程的方法是有区别的,Sleep()使得线程立即停止执行,但是在调用Suspend()方法之前,公共语言运行时必须到达一个安全点。一个线程不能对另外一个线程调用Sleep()方法,但是可以调用Suspend()方法使得另外一个线程暂停执行。对已经挂起的线程调用Thread.Resume()方法会使其继续执行。不管使用多少次Suspend()方法来阻塞一个线程,只需一次调用Resume()方法就可以使得线程继续执行。已经终止的和还没有开始执行的线程都不能使用挂起。Thread.Sleep(int x)使线程阻塞x毫秒。只有当该线程是被其他的线程通过调用Thread.Interrupt()或者Thread.Abort()方法,才能被唤醒。如果对处于阻塞状态的线程调用Thread.Interrupt()方法将使线程状态改变,但是会抛出ThreadInterupptedException异常,你可以捕获这个异常并且做出处理,也可以忽略这个异常而让运行时终止线程。在一定的等待时间之内,Thread.Interrupt()和Thread.Abort()都可以立即唤醒一个线程。

      该方法适合需要长时间运行的程序,不适合比较简单的操作。

    2、System.Threading.ThreadPool

    public void TestThreadPool()
    {
        ThreadPool.QueueUserWorkItem(_ => BackgroundMethod("Test ThreadPool"));
    }

      如果线程的数目并不是很多,而且想控制每个线程的细节诸如线程的优先级等,使用Thread是比较合适的;但是如果有大量的线程,考虑使用线程池应该更好一些,它提供了高效的线程管理机制来处理多任务。

      当创建应用程序时,应该认识到大部分时间你的线程在空闲的等待某些事件的发生(诸如按下一个键或侦听套节子的请求)。毫无疑问的,你也会认为这是绝对的浪费资源。 

        如果这里有很多的任务需要完成,每个任务需要一个线程,应该考虑使用线程池来更有效的管理你的资源并且从中受益。线程池是执行的多个线程集合,它允许添加以线程自动创建和开始的任务到队列里面去。使用线程池使得系统可以优化线程在CPU使用时的时间碎片。但是要记住在任何特定的时间点,每一个进程和每个线程池只有一个一个正在运行的线程。这个类使得线程组成的池可以被系统管理,而使主要精力集中在工作流的逻辑而不是线程的管理。 

        当第一次实例化ThreadPool类时线程池将被创建。它有一个默认的上限,即每处理器最多可以有25个,但是这个上限是可以改变的。这样使得处理器不会闲置下来。如果其中一个线程等待某个事件的发生,线程池将初始化另外一个线程并投入处理器工作,线程池就是这样不停的创建工作的线程和分配任务给那些没有工作的在队列里的线程。唯一的限制是工作线程的数目不能超过最大允许的数目。每个线程将运行在默认的优先级和使用默认的属于多线程空间的堆栈大小空间。一旦一项工作任务被加入队列,是不能取消的。

    3、System.IAsyncResult

    public void TestIAsyncResult()
    {
        BackgroundMethodDelegate x = new BackgroundMethodDelegate(BackgroundMethod);
        IAsyncResult a = x.BeginInvoke("Test IAsyncResult", null, null);
        x.EndInvoke(a);
    }

      IAsyncResult 接口由包含可异步操作的方法的类实现。 它是启动一个异步操作,例如, FileStream.BeginRead,并将其传递到方法推断异步操作,例如 FileStream.EndRead方法的返回类型。 当异步操作完成时,IAsyncResult 对象也将传递给由 AsyncCallback 委托调用的方法。支持 IAsyncResult 接口的对象存储异步操作的状态信息,并提供同步对象以允许线程在操作完成时终止。

    4、System.ComponentModel.BackgroundWorker 

    public void TestBackgroundWorker()
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate { BackgroundMethod("Test BackgroundWorker"); };
        worker.RunWorkerAsync();
    }

    BackgroundWorker是.net里用来执行多线程任务的控件,它允许编程者在一个单独的线程上执行一些操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面 (UI) 始终处于停止响应状态。如果您需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用BackgroundWorker类方便地解决问题。 

    该控件有三个事件:DoWork 、ProgressChanged 和 RunWorkerCompleted。在程序中调用RunWorkerAsync方法则会启动DoWork事件的事件处理,当在事件处理过程中,调用 ReportProgress方法则会启动ProgressChanged事件的事件处理,而当DoWork事件处理完成时,则会触发RunWorkerCompleted事件。必须非常小心,确保在 DoWork 事件处理程序中不操作任何用户界面对象。而应该通过 ProgressChanged和 RunWorkerCompleted 事件与用户界面进行通信。 

    5、System.Threading.Timer

    public void TestTimer()
    {
        Timer timer = new Timer(_ => BackgroundMethod("Test Timer"), null, 0, Timeout.Infinite);
    }

      在系统开发过程中经常用到定时器进行定时处理,比如比较常见的邮件群发、实时更新论坛的在线人数、文章数、点击率等。很多情况下,我们不能对某一状态或者某一行为进行实时监控,所以就希望系统能够实现这一功能。通过多线程技术可以使得定时器的性能更高。

      尽管定时器能够自动处理或者一些批处理操作,但是定时器也给系统带来一定的安全隐患,特别是当定时进行的操作出现bug时,如果没有对Exception做出及时的处理,系统资源将会大大的浪费,严重的情况下,可能导致系统崩溃。因此,对于定时器的使用一定要慎重,至少要保证定时处理的行为出现异常的可能性很小,并在出现Exception的情况下及时处理。

      Timer是一个非常常用的定时器类,是一个使用回调方法的计时器,而且由线程池线程服务,简单且对资源要求不高。 

    6、System.Threading.Tasks.Task(1)

    public void TestTask1()
    {
        using (Task task = new Task(() => BackgroundMethod("Test Task 1")))
        {
            task.Start();
            task.Wait();
        }
    }

    在C#4.0之前需要执行一个复杂的异步操作时,只能使用CLR线程池技术来执行一个任务。线程池执行异步任务时,不知道任务何时完成,以及在任务完成后不能获取到返回值。但是在C#4.0中引人了一个任务(System.Threading.Tasks命名空间的类型)机制来解决异步操作完成时间和完成后返回值的问题。

    7、System.Threading.Tasks.Task(2) 

    public void TestTask2()
    {
        Task.Factory.StartNew(() => BackgroundMethod("Test Task 2"));
    }

    8、System.Threading.Tasks.Parallel

    public void TestParallel()
    {
        Parallel.Invoke(() => BackgroundMethod("Test Parallel"));
    }

      Parallel提供对并行循环和区域的支持。它的所有公共且受保护的成员都是线程安全的,可从多个线程同时使用。

    9、System.Diagnostics.ProcessStartInfo

    public void TestProcessStartInfo()
    {
        ProcessStartInfo startInfo = new ProcessStartInfo("StartThreads.exe", "ProcessStartInfo");
        startInfo.CreateNoWindow = false;
        startInfo.UseShellExecute = false;
        startInfo.RedirectStandardOutput = true;
    
        Process process = Process.Start(startInfo);
        Console.WriteLine("Approach "{0}" sent from second process",
            process.StandardOutput.ReadToEnd());
        process.WaitForExit();
    } 

      ProcessStartInfo 与 Process 组件一起使用。使用 Process 类启动进程时,您还可以访问附加到当前运行进程时可用的进程信息之外的进程信息。

      可以使用 ProcessStartInfo 类来更好地控制您启动的进程。至少必须以手动方式或使用构造函数来设置 FileName 属性。文件名是任何应用程序或文档。此处,将文档定义为具有与其关联的打开或默认操作的任何文件类型。您可以通过操作系统的“文件夹选项”对话框来查看计算机的已注册文件类型及其关联应用程序。单击“高级”按钮可打开一个对话框,该对话框显示是否存在与特定的注册文件类型相关联的打开操作。

      另外,还可以设置定义要对该文件执行的操作的其他属性。可以为 Verb 属性指定特定于 FileName 属性的类型的值。例如,可以为文档类型指定“print”。另外,还可以指定Arguments 属性值,这些值将成为传递给文件的打开过程的命令行参数。例如,如果在 FileName 属性中指定一个文本编辑器应用程序,则可以使用 Arguments 属性指定将用该编辑器打开的一个文本文件。

      标准输入方式通常为键盘,标准输出和标准错误通常显示在监视器屏幕上。但是,可以使用 RedirectStandardInputRedirectStandardOutput 和 RedirectStandardError 属性使进程从文件或其他设备获取输入,或者向文件或其他设备返回输出。如果使用 Process 组件上的 StandardInputStandardOutput 或 StandardError 属性,则必须首先在 ProcessStartInfo属性上设置相应的值。否则,从流读取或向流写入时,系统会引发异常。

      设置 UseShellExecute 以指定是否使用操作系统外壳程序启动进程。

      可以更改任何 ProcessStartInfo 属性的值,直到进程启动为止。启动进程后,更改这些值没有效果。

      上述代码中的后台线程启动的方法为:

    private delegate void BackgroundMethodDelegate(string approach);
    
    private void BackgroundMethod(string approach)
    {
        Console.WriteLine("Approach "{0}" Thread Id:{1} ({2})",
            approach,
            Thread.CurrentThread.ManagedThreadId,
            Thread.CurrentThread.IsThreadPoolThread ? "from ThreadPool" : "not from ThreadPool");
    }

      参考:http://www.wangqi.com/html/2007-01/9480.htm

           http://tech.it168.com/oldarticle/2007-04-26/200704262145953.shtml

           http://www.360doc.com/content/11/0812/11/1039473_139824496.shtml

           http://www.th7.cn/Program/net/201212/117931.shtml

           http://msdn.microsoft.com/zh-cn/library/system.threading.tasks.parallel.aspx

  • 相关阅读:
    线程池:第二章:线程池实际中使用哪一个
    实战:第五章:EZDML修改数据类型
    面试:第十四章:大厂中高级java程序员面试题
    服务网关Zuul路由转发
    启动zipkin服务
    安装Elasticsearchhead
    Zuul 路由熔断
    Unable to find local grunt.
    Kibana安装
    sleuth zipkin链路追踪
  • 原文地址:https://www.cnblogs.com/hibernation/p/3338323.html
Copyright © 2020-2023  润新知