任务,线程和同步(五)之(Thread类)线程类

来源:互联网 发布:端口聚合命令 编辑:程序博客网 时间:2024/06/16 13:02

Thread类

如果想要更多的控制,可以使用Thread类,该类允许创建前台线程,以及设置线程的优先级。
使用Thread类可以创建和控制线程。下面的代码是创建和启动一个新线程的简单例子。

  static void Main()        {            var t1 = new Thread(ThreadMain);            t1.Start();            Console.WriteLine("is  ThreadMain IsThreadPoolThread?:{0}", t1.IsBackground);             Console.WriteLine("this is the main thread");            Console.ReadKey();        }        static void ThreadMain()        {            Console.WriteLine("running in a thread");        }

不能保证哪个结果先输出。线程由操作系统调度,每次哪个线程在面前可以不同。

这里写图片描述

在Thread中,还可以和lambda表达式,异步委托一起使用。

  static void Main()        {            var t1 = new Thread(() => Console.WriteLine("running in a thread,id:{0}", Thread.CurrentThread.ManagedThreadId));            t1.Start();            Console.WriteLine("this is the main thread,id:{0}",Thread.CurrentThread.ManagedThreadId);            Console.ReadKey();        }

这里写图片描述

6给线程传递数据
给线程传递一些数据可以采用2种方式。

  • 一种是使用带ParameterizedThreadStart委托参数的Thread构造函数,
  • 另一种是创建一个自定义类,把线程的方法定义为实例方法,这样就可以初始化实例的数据,之后启动线程。

    要给线程传递数据,需要某个存储数据的类或结构。这里定义了Data结构,但可以传递任意对象。

 public struct Data        {            public string Message;        }

如果使用了ParameterizedThreadStart委托,线程的入口点必须有一个object类型的参数,且返回类型必须为void。对象可以强制转换为任意数据类型。

ParameterizedThreadStart委托

这里写图片描述

给委托定义的方法

     static void ThreadMainWithPar(object o)        {            Data d = (Data)o;            Console.WriteLine("running in a thread ,received {0}",d.Message);        }

通过Thread类的构造函数,可以将新的入口点赋予ThreadMainWithPar,传递变量O,以调用Start();

   static void Main()        {            var d = new Data { Message="Info"};            var t1 = new Thread(ThreadMainWithPar);            t1.Start(d);            Console.ReadKey();        }

这里写图片描述

给新线程传递数据的另一个方式是定义一个类,在其中定义需要的字段,将线程的主方法定义为一个实例方法。下面新建一个MyThread的一个对象,给Thread类的构造函数传递对象和ThreadMain方法,线程可以访问数据。

 class MyThread    {        private string data;        public MyThread(string data)        {            this.data = data;        }        public void ThreadMain()        {            Console.WriteLine("running in a thread ,data:{0}",data);        }    }    class Program    {        /// <summary>        /// 应用程序的主入口点。        /// </summary>        static void Main()        {            MyThread d = new MyThread("ffff");            Thread t1 = new Thread(d.ThreadMain);            t1.Start();            Console.ReadKey();        }}

这里写图片描述

2.后台线程

只要有一个前台线程在运行,应用程序的进程就在运行。如果多个前台线程在运行,而main()方法结束了,应用程序的进程就仍然是激活的,直到所有前台线程完成其任务为止。
在默认情况下,用Thread类创建的线程前台线程,线程池中的线程总是后台线程。
当然Thread类,可以设置该线程是否是前台线程,还是后台线程,通过设置IsBackGroud属性为ture或false,默认属性设置为fasle

 static void Main()        {            var t1 = new Thread(ThreadMain111) { Name="mynewthread",IsBackground=false};            t1.Start();            Console.WriteLine("main thread ending now");        }        static void ThreadMain111()        {            Console.WriteLine("Thread {0} started",Thread.CurrentThread.Name);            Thread.Sleep(3000);            Console.WriteLine("Thread {0} ended", Thread.CurrentThread.Name);        }

尽管主线程会提前完成其工作,但在启动应用程序时,会看到写入控制台的完成信息。原因是新线程也是一个前台线程。
这里写图片描述

当把IsBackground属性改为True时候,控制台显示不同的结果。

这里写图片描述

后台线程非常适用于完成后台任务。例如,如果关闭Word应用程序,拼写检查器继续运行其进程就没有意义。在关闭应用程序时,拼写检查器线程就可以关闭。但是,组织OutLook信息库的线程应一直是激活的,直到关闭OutLook。它才结束。

3.线程优先级
线程由操作系统调度,给线程指定优先级,就可以影响调度顺序。在改变优先级之前,必须理解线程调度器。操作系统根据优先级来调度线程。调度优先级最高的线程以在cpu上运行。线程如果在等待资源,它就会停止运行,并释放CPU。
线程必须等待时有几个原因,例如响应睡眠指令,等待磁盘I/O的完成,等待网络包的到达等等。如果线程不是主动释放CPU,线程调度器就会抢占该线程。线程由一个时间量,这意味着它可以持续使用CPU,知道这个时间到达(这里没有更高优先级的线程)。如果优先级相同的多线程等待使用cpu,线程调度器就会使用一个循环调度规则,将CPU逐个交个线程使用。如果线程被其他线程抢占,它就排在队列的最后。
只有优先级相同的多个线程在运行,才用得上时间量和循环规则。优先级是动态的。如果线程是CPU密集型(一直需要cpu,且不等待资源),其优先级就降低为用该线程定义的基本优先级。如果线程在等待资源,它的优先级会提高。由于优先级的提高,线程会很有可能在下次等待时获得CPU。
在Thread类中,可以设置Priority属性,来影响线程的基本优先级。
这里写图片描述
注:在给线程指定较高优先级时要小心,因为这可能降低其他线程的运行概率。根据需要,可以短暂地改变优先级。

4.控制线程

  • 调用Thread对象的Start方法,可以创建线程。但是在调用Start方法之后,新线程仍不是处于Running状态,而是处于Unstarted状态。只要操作系统的线程调度器选择了要运行的线程,线程就会改为Running状态。读取Tread.ThreadState属性,就可以获取线程的当前的状态。
  • 使用Thread.Sleep方法,会使线程处于WaitSleepJoin状态。在等待Sleep方法定义的时间段后,线程就会再次被唤醒。
  • 要停止另一个线程,就可以调用Thread.Abort方法。调用这个方法时,会在接到终止命名的线程中抛出一个ThreadAbortException类型的异常。用于一个处理程序捕获这个异常,线程可以在结束前完成一些清理工作。如果调用了Thread.ResetAbort线程还有机会在接受到ThreadAbortException异常后继续运行,如果线程没有重置终止,接受到终止请求的线程的状态就从AbortRequeated改为Aborted。
  • 如果需要等待线程的结束,就可以调用Thread.Join方法。Thread.Join停止当前线程,并把它设置为WaitSleepJoin状态,直到加入线程完成为止。
0 0