zoukankan      html  css  js  c++  java
  • 多线程开发技术基础

    多线程开发技术基础

    多线程开发扫盲系列第二编:多线程开发技术基础

    1. 线程的创建、启动和停止   

    2. Windows操作系统线程调度策略   

    3. 向线程函数传送信息的方式   

    4. 线程统一取消模型   

    1.线程的创建、启动和停止

    在.NET应用程序中,线程由Thread类创建的对像代表。Thread类提供了许多属性和方法对线程进行控制
    Thread类拥有4个重载的构造函数,最常用的一个可接收一个ThreadStart类型的参数:public Thread(ThreadStart start)
    ThreadStart是一个委托,其定义如下:
    Public delegate void ThreadStart();
    从以上定义可知,在创建线程对像时必须传给它一个方法,此方法无参数且不返回从任何值。这个方法被称为”线程方法”,由于在面向对像程序中,方法本质上就是一个函数,因此人们习惯地又将”线程方法”称为”线程函数”,每个线程都对应着一个特定的线程函数,线程的执行体现着线程函数的执行
    线程函数可以是静态的,也可以是实例的
    Class MyThread
    {
    Public static void ThreadMethod1(){ ......}
    Public void ThreadMethod2(){......}
    }

    //将静态方法当作线程函数
    Thread th1=new Thread(MyThread.ThreadMethod1);
    //将实例方法当作线程函数
    MyThread obj=new MyThread();
    Thread th2=new Thread(obj.ThreadMethod2);
    当线程对像创建以后,调用它的Start方法启动线程
    Th1.Start();

    线程终止可以使用线程对像的Abort方法。
    这时线程对像自身会引发一个ThreadAboutException异常。代码如:
    namespace ThreadAbort
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("主线程开始");
                MyThread mythread = new MyThread();
                Thread th = new Thread(mythread.SomeLongTask);
               // th.IsBackground = true;
                th.Start();
                Thread.Sleep(300);
                Console.WriteLine("主线程调用abort方法提前终止辅助线程");
                th.Abort();
                Console.WriteLine("主线程结束");

                Console.ReadKey();
            }
        }

        class MyThread
        {
            public void SomeLongTask()
            {
                try
                {
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine(i);
                        Thread.Sleep(100);
                    }
                }
                catch (ThreadAbortException e)
                {
                    Console.WriteLine("辅助线程被提前中断"+e.Message);
                    Thread.ResetAbort();
                }
                finally
                {
                    Console.WriteLine("完成清理辅助线程的工作");
                }
                Console.WriteLine("辅助线程结束");
            }
        }
    }
    Background属性为true的线程称为"背景线程",即主线程结束时让CLR自动地强行结束所有还在运行的辅助线程


    可以使用Thread.Sleep方法让线程对像休息一段特定的时间后再继续(称为线程休眠)
    Thread.Sleep(3000);
    Thread.Sleep方法的调用将导致线程从running状态转换为waitsleepjoin状态,这将导致一个线程上下文的切换,因而会对程序性能有一定影响。如果仅仅是希望线程能等待一定的时间,即让线程在一段时间内空转,可以调用Thread.SpinWait。

    等待一个线程的完成
    主线程启动一个辅助线程后,要等待它运行结束后才能继续干某些工作
    线程之间的这种协作关系称为”线程同步”
    有关JOIN的方法用图来分析,在线程A的执行流程中调用线程B的JOIN方法,其实是相当于把线程B的整个执行流程”嵌入”线程A的执行流程中。线程A会在调用JOIN方法的那句代码处等待线程B的工作完成。因此,在这种情况下必须保证线程B是可以在有限时间内结束的,可以想像整个程序会始处于等待状态。这种状态称为”死锁”。

    2.Windows操作系统线程调度策略

    在.net中每个托管线程都拥有一系列的状态,在任何时候每个线程对像一定处于一个确定的状态之中,可通过Thread类的ThreadState属性了解线程对像所处的状态
    线程状态。

    线程状态

    说明

    Abort

    线程处于Stopped状态中

    AbortRequested

    已对线程调用了Thread.Abort方法,但线程尚未接收到试图终止它挂起的Threading.ThreadAbortException

    Background

    线程正作为后台线程执行,相对于前台线程而言

    Running

    线程已启动,正在运行中

    Stopped

    线程已停止

    StopRequested

    正在请求线程停止,仅在.Net Framework内部使用

    Suspended

    线程已挂起

    SuspendRequested

    正在请求线程挂起

    Unstarted

    尚未对线程调用Thread.Start方法

    WaitSleepJoin

    由于调用Montor.Enter申请锁,Sleep或Join线程已被阻止

    线程优先级
    每个线程还关联着一个线程优先级,由Thread.Priority属性标识
    .NET Framwork提供了五种线程优先级,从高到低依次为
    Highest—>AboveNormal->Normal->BelowNormal->Lowest
    新创建的线程对像具体优先级”Normal”,但可以将它的Priority属性更改为上述的任何一个值。


    操作系统线程调度策略
    Windows操作系统按照时间片来将CPU分配给各个线程使用,由于可能有多个线程正处于Running状态,Windows就按照线程优先级将等待分配的CPU线程分成几个队列,根据特定的线程调度算法从队列中选一个线程运行。当高优先级队列中还有线程等待分配CPU时,低优先级的线程就只能等待了。如果高优先级的线程都已执行完毕,则线程调度程序为低优先级线程队列中的线程分配CPU运行。如果某一线程正在CPU执行,这时windows发现一个高优先级的线程进入就绪队列等待运行,则在当前这个低优先级的线程执行完一个时间片后,操作系统会进行一次线程调度,以让这个新到的高优先级线程有机会获取CPU运行。
    一个线程优先级不直接影响该线程的状态,只影响windows选中它占用 CPU运行的时间的概率。

    3.向线程函数传送信息的方式

    使用外壳方法
    线程类Thread接收一个ThreadStart委托类型的参数,而ThreadStart不能有返回值,也没有参数。这就大大限制了线程函数的选择,因此不得不手动增加一个符合ThreadStart委托要求的"外壳"方法,示例如:

        class Program
        {
            static void Main(string[] args)
            {
                MyThread2 obj = new MyThread2 { x = 10, y = 20 };
                Thread th = new Thread(obj.ThreadMethod);
                th.Start();
                th.Join();
                Console.WriteLine("结果为:" + obj.returnValue.ToString());
                Console.ReadKey();

            }
        }
        class MyThread2
        {
            public int x;
            public int y;
            public long returnValue;
            public void ThreadMethod()
            {
                returnValue = SomeFunc(x, y);
            }
            
            long SomeFunc(int x, int y)
            {
                long ret = 0;
                return ret = x + y;
            }

        }

    使用带参数的ParameterIzedThreadStart
    .NET2.0给Thread类加入一个重载形式
    public Thread(ParameterizedThreadStart start);
    ParameterizedThreadStart委托可以接收含有一个参数且无返回值的线程函数,由于其参数类型为Object,所以此委托可接收任何类型的方法参数。示例如:
    class MyThread
    {
        public void Method1(object x){......}
        public void Method2(object str){......}
    }
    Thread th1=new Thread(new ParameterizedThreadStart(Method1));
    Thread th2=new Thread(new ParameterizedThreadStart(Method2));
    th1.Start(100);
    th2.Start("Hello");
    使用ParameterIzedThreadStart委托,可以避免将线程函数的参数外化为类的字段,但还是有限制,即纯种函数只能有一个参数,且不能有返回值

    设计线程信息输入输出辅助类
    见下方示例代码,代码一看都明白了。

    namespace UseArray
    {
        class Program
        {
            static void Main(string[] args)
            {
                ThreadMethodHelper argu = new ThreadMethodHelper();
                argu.arr = new int[] { -1, 9, 100, 78, 20, 56 };
                Thread th = new Thread(DoWithArray);
                th.Start(argu);
                th.Join();
                Console.WriteLine("数组元素清单");
                for (int i = 0; i < argu.arr.Length; i++)
                {
                    Console.WriteLine(i + " ");
                }
                Console.ReadKey();
            }
            static void DoWithArray(object obj)
            {
                ThreadMethodHelper argu = obj as ThreadMethodHelper;
                for (int i = 0; i < argu.arr.Length; i++)
                {
                    if (argu.arr[i] > argu.MaxVal)
                        argu.MaxVal = argu.arr[i];
                    if (argu.arr[i] < argu.MinVal)
                        argu.MinVal = argu.arr[i];
                    argu.Sum += argu.arr[i];
                }
                argu.Average = argu.Sum / argu.arr.Length;
            }
        }
        class ThreadMethodHelper
        {
            public int[] arr;
            public int MaxVal=0;
            public int MinVal=0;
            public long Sum=0;
            public double Average = 0;

        }
    }

    4. 线程统一取消模型

    在多线程程序运行中,由用户取消操作是一种非常常见的场景,比如用户使用windows资源管理器在当前文件夹中搜索文件时,可以通过点击其它文件夹而取消搜索。
    中途停止一个线程的执行,通常用Thread.Abort方法,但这种方式会造成程序涉及的数据完整性受到破坏,线程所占用的一些系统资源(比如文件句柄等)也可能无法完成。比较合理的方式是外界提出"取消操作"的请求,然后由线程自身来决定如何处理这一请求。
    在设计多线程程序时,可设置一个用于接收外部取消消息的属性,然后在线程函数中分阶段地检测这一属性,每个阶段的检查点由软件开发者确定,并且决定线程如何优雅退出。

  • 相关阅读:
    创建Variant数组
    ASP与存储过程(Stored Procedures)
    FileSystemObject对象成员概要
    Kotlin 朱涛9 委托 代理 懒加载 Delegate
    Kotlin 朱涛 思维4 空安全思维 平台类型 非空断言
    Kotlin 朱涛7 高阶函数 函数类型 Lambda SAM
    Kotlin 朱涛16 协程 生命周期 Job 结构化并发
    Proxy 代理模式 动态代理 cglib MD
    RxJava 设计理念 观察者模式 Observable lambdas MD
    动态图片 Movie androidgifdrawable GifView
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3292014.html
Copyright © 2011-2022 走看看