Thread类(线程)

操作系统通过线程对程序的执行进行管理,当操作系统运行一个程序的时候,首先,操作系统将为这个准备运行的程序分配一个进程,以管理这个程序所需要的各种资源。在这些资源之中,会包含一个称为主线程的线程数据结构,用来管理这个程序的执行状态。

  在Windows操作系统下,线程的的数据结构包含以下内容:

  1、线程的核心对象:主要包含线程当前的寄存器状态,当操作系统调度这个线程开始运行的时候,寄存器的状态将被加载到CPU中,重新构建线程的执行环境,当线程被调度出来的时候,最后的寄存器状态被重新保存到这里,已备下一次执行的时候使用。
  2、线程环境块(Thread Environment Block,TED):是一块用户模式下的内存,包含线程的异常处理链的头部。另外,线程的局部存储数据(Thread Local Storage Data)也存在这里。
  3、用户模式的堆栈:用户程序的局部变量和参数传递所使用的堆栈,默认情况下,Windows将会被分配1M的空间用于用户模式堆栈。
  4、内核模式堆栈:用于访问操作系统时使用的堆栈。

在抢先式多任务的环境下,在一个特定的时间,CPU将一个线程调度进CPU中执行,这个线程最多将会运行一个时间片的时间长度,当时间片到期之后,操作系统将这个线程调度出CPU,将另外一个线程调度进CPU,我们通常称这种操作为上下文切换。

  在每一次的上下文切换时,Windows将执行下面的步骤:

  • 将当前的CPU寄存器的值保存到当前运行的线程数据结构中,即其中的线程核心对象中。
  • 选中下一个准备运行的线程,如果这个线程处于不同的进程中,那么,还必须首先切换虚拟地址空间。
  • 加载准备运行线程的CPU寄存器状态到CPU中。

  公共语言运行时CLR(Common Language Runtime)是.Net程序运行的环境,它负责资源管理,并保证应用和底层操作系统之间必要的分离。

  在.Net环境下,CLR中的线程需要通过操作系统的线程完成实际的工作,目前情况下,.Net直接将CLR中的线程映射到操作系统的线程进行处理和调度,所以,我们每创建一个线程将会消耗1M以上的内存空间。但未来CLR中的线程并不一定与操作系统中的线程完全对应。通过创建CLR环境下的逻辑线程,我们可能创建更加节省资源的线程,使得大量的CLR线程可以工作在少量的操作系统线程之上。

 

1. System.Threading.Thread类

System.Threading.Thread是用于控制线程的基础类,通过Thread可以控制当前应用程序域中线程的创建、挂起、停止、销毁。

它包括以下常用公共属性:

属性名称说明
CurrentContext获取线程正在其中执行的当前上下文。
CurrentThread获取当前正在运行的线程。
ExecutionContext获取一个 ExecutionContext 对象,该对象包含有关当前线程的各种上下文的信息。
IsAlive获取一个值,该值指示当前线程的执行状态。
IsBackground获取或设置一个值,该值指示某个线程是否为后台线程。
IsThreadPoolThread获取一个值,该值指示线程是否属于托管线程池。
ManagedThreadId获取当前托管线程的唯一标识符。
Name获取或设置线程的名称。
Priority获取或设置一个值,该值指示线程的调度优先级。
ThreadState获取一个值,该值包含当前线程的状态。

 常用属性示例:

using System;
using System.Threading;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //新建3个线程并设定各自的优先级
            Thread t1 = new Thread(Run);
            t1.Priority = ThreadPriority.Normal;
            t1.Start();

            Console.ReadKey();
        }

        public static void Run()
        {
            Thread t1 = Thread.CurrentThread;   //静态属性,获取当前执行这行代码的线程
            Console.WriteLine("我的优先级是:" + t1.Priority);
            Console.WriteLine("我是否还在执行:" + t1.IsAlive);
            Console.WriteLine("是否是后台线程:" + t1.IsBackground);
            Console.WriteLine("是否是线程池线程:" + t1.IsThreadPoolThread);
            Console.WriteLine("线程唯一标识符:" + t1.ManagedThreadId);
            Console.WriteLine("我的名称是:" + t1.Name);
            Console.WriteLine("我的状态是:" + t1.ThreadState);
        }
    }
}
View Code

 

2. 线程的标识符

ManagedThreadId是确认线程的唯一标识符,程序在大部分情况下都是通过Thread.ManagedThreadId来辨别线程的。而Name是一个可变值,在默认时候,Name为一个空值 Null,开发人员可以通过程序设置线程的名称,但这只是一个辅助功能。

 

3. 线程的优先级别

.NET为线程设置了Priority属性来定义线程执行的优先级别,里面包含5个选项,其中Normal是默认值。除非系统有特殊要求,否则不应该随便设置线程的优先级别。

成员名称说明
Lowest可以将 Thread 安排在具有任何其他优先级的线程之后。
BelowNormal可以将 Thread 安排在具有 Normal 优先级的线程之后,在具有 Lowest 优先级的线程之前。
Normal默认选择。可以将 Thread 安排在具有 AboveNormal 优先级的线程之后,在具有BelowNormal 优先级的线程之前。
AboveNormal可以将 Thread 安排在具有 Highest 优先级的线程之后,在具有 Normal 优先级的线程之前。
Highest可以将 Thread 安排在具有任何其他优先级的线程之前。

 优先级的示例:

using System;
using System.Threading;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //新建3个线程并设定各自的优先级
            Thread t1 = new Thread(Run);
            t1.Priority = ThreadPriority.Lowest;
            Thread t2 = new Thread(Run);
            t2.Priority = ThreadPriority.Normal;
            Thread t3 = new Thread(Run);
            t3.Priority = ThreadPriority.Highest;
            //由低到高优先级的顺序依次调用
            t1.Start();
            t2.Start();
            t3.Start();
            Console.ReadKey();
        }

        public static void Run()
        {
            Console.WriteLine("我的优先级是:" + Thread.CurrentThread.Priority);
        }
    }
}
View Code

4. 线程的状态

通过ThreadState可以检测线程是处于Unstarted、Sleeping、Running 等等状态,它比 IsAlive 属性能提供更多的特定信息。

前面说过,一个应用程序域中可能包括多个上下文,而通过CurrentContext可以获取线程当前的上下文。

CurrentThread是最常用的一个属性,它是用于获取当前运行的线程。

 

5. System.Threading.Thread的方法

Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,以后来的例子中会经常使用。

方法名称说明
Abort()    终止本线程。
GetDomain()返回当前线程正在其中运行的当前域。
GetDomainId()返回当前线程正在其中运行的当前域Id。
Interrupt()中断处于 WaitSleepJoin 线程状态的线程。
Join()已重载。 阻塞调用线程,直到某个线程终止时为止。
Resume()继续运行已挂起的线程。
Start()  执行本线程。
Suspend()挂起当前线程,如果当前线程已属于挂起状态则此不起作用
Sleep()  把正在运行的线程挂起一段时间。

6. 开发实例

 前台线程与后台线程的区别

  我们看到上面有个属性叫后台线程,非后台线程就叫前台线程吧,Thread.Start()启动的线程默认为前台线程,启动程序时创建的主线程一定是前台线程。应用程序与必须等到所有的前台线程执行完毕才会卸载。而当IsBackground设置为true时,就是后台线程了,当主线程执行完毕后就直接卸载,不再理会后台线程是否执行完毕。

  前台与后台线程的设置必须在线程启动之前进行设置,线程启动之后就不能设置了。

Thread创建的线程是前台线程,线程池中的是后台线程。

1、启动线程(.Start)
  1.1、无参数的调用
   Thread t = new Thread(Action);
   t.Start();
  1.2、带参数的调用方式一
   Thread t = new Thread(Action<>);
   t.Start(参数);
  1.3、带参数的调用方式二
   Thread t = new Thread(() => Action<>(参数));
   t.Start();
  1.4、如果遇到需要返回值,就可以考虑用异步;
   public delegate string MethodCaller(string name);//定义个代理 
   MethodCaller mc = new MethodCaller(GetName); 
   string name = "my name";//输入参数 
   IAsyncResult result = mc.BeginInvoke(name,null, null); 
   string myname = mc.EndInvoke(result);//用于接收返回值 
 
   public string GetName(string name)    // 函数
   {
      return name;
   }
using System;
using System.Threading;

namespace ConsoleApp1
{
    public delegate string MethodCaller(string name);//定义个代理 

    class Program
    {
        static void CountNumbers()
        {
            int _iterations = 3;
            for (int i = 1; i <= _iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
        }

        static void Count(object iterations)
        {
            CountNumbers((int)iterations);
        }

        static void CountNumbers(int iterations)
        {
            for (int i = 1; i <= iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
        }

        static void PrintNumber(int number)
        {
            Console.WriteLine(number);
        }

        static string GetName(string name)    // 函数
        {
            int _iterations = 5;
            for (int i = 1; i <= _iterations; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
                Console.WriteLine("{0} prints {1}", Thread.CurrentThread.Name, i);
            }
            return name;
        }

        static void Main(string[] args)
        {
            //无参数的调用
            var threadOne = new Thread(CountNumbers);
            threadOne.Name = "ThreadOne";
            threadOne.Start();
            threadOne.Join();
            Console.WriteLine("--------------------------");

            //带参数的调用方式一
            var threadTwo = new Thread(Count);
            threadTwo.Name = "ThreadTwo";
            threadTwo.Start(3);
            threadTwo.Join();
            Console.WriteLine("--------------------------");

            //带参数的调用方式二
            var threadThree = new Thread(() => CountNumbers(4));
            threadThree.Name = "ThreadThree";
            threadThree.Start();
            threadThree.Join();
            Console.WriteLine("--------------------------");

            //注意在多个lambda表达式中使用香港的变量,它们会共享该变量 这里两个线程的参数都是20
            int i = 10;
            var threadFour = new Thread(() => PrintNumber(i));
            i = 20;
            var threadFive = new Thread(() => PrintNumber(i));
            threadFour.Start();
            threadFive.Start();
            Console.WriteLine("--------------------------");

            MethodCaller mc = new MethodCaller(GetName);
            string name = "my name";//输入参数 
            IAsyncResult result = mc.BeginInvoke(name, null, null);
            Console.WriteLine("继续主线程的业务");
            string myname = mc.EndInvoke(result);//用于接收返回值 
            Console.WriteLine(string.Format("result={0}", myname));
        }
    }
}
View Code

2、等待线程结束,会阻塞当前主线程(.Join)

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting program...");
            Thread t = new Thread(PrintNumbersWithDelay);
            t.Start();
            t.Join();
            Console.WriteLine("Thread completed");
        }

        static void PrintNumbersWithDelay()
        {
            Console.WriteLine("Starting...");
            for (int i = 1; i <= 5; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine(i);
            }
        }
    }
}
View Code

3、线程中异常的处理只能在线程调用中的函数里面去处理,在外面是接收不到线程中的报错的

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var t = new Thread(FaultyThread);
            t.Start();
            t.Join();

            //try
            //{
            //    t = new Thread(BadFaultyThread);
            //    t.Start();
            //}
            //catch (Exception ex)
            //{
            //    //这里捕获不了线程中的异常
            //    Console.WriteLine("We won't get here!");
            //}
        }

        static void BadFaultyThread()
        {
            Console.WriteLine("Starting a Badfaulty thread...");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            throw new Exception("Boom!");
        }

        static void FaultyThread()
        {
            try
            {
                Console.WriteLine("Starting a faulty thread...");
                Thread.Sleep(TimeSpan.FromSeconds(1));
                throw new Exception("Boom!");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception handled: {0}", ex.Message);
            }
        }
    }
}
View Code

4、设置线程优先级(.Priority)

using System;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Current thread priority: {0}", Thread.CurrentThread.Priority);
            Console.WriteLine("Running on all cores available");
            RunThreads();
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine("Running on a single core");
            Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
            RunThreads();
            Console.ReadLine();
        }

        static void RunThreads()
        {
            var sample = new ThreadSample();

            var threadOne = new Thread(sample.CountNumbers);
            threadOne.Name = "ThreadOne";
            var threadTwo = new Thread(sample.CountNumbers);
            threadTwo.Name = "ThreadTwo";

            threadOne.Priority = ThreadPriority.Lowest;
            threadTwo.Priority = ThreadPriority.Highest;
            threadOne.Start();
            threadTwo.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));
            sample.Stop();
        }

        class ThreadSample
        {
            private bool _isStopped = false;

            public void Stop()
            {
                _isStopped = true;
            }

            public void CountNumbers()
            {
                long counter = 0;

                while (!_isStopped)
                {
                    counter++;
                }

                Console.WriteLine("{0} with {1,11} priority " +
                            "has a count = {2,13}", Thread.CurrentThread.Name,
                            Thread.CurrentThread.Priority,
                            counter.ToString("N0"));
            }
        }
    }
}
View Code

 

 

转载于:https://www.cnblogs.com/scmail81/p/9297068.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值