目录

一、概述

二、线程的创建

三、线程的休眠

四、线程的等待

五、线程的终止

六、线程的状态

七、线程的优先级


一、概述

线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。

在任务管理器中,可以查看当前 CPU 运行的线程个数

C# 的多线程的开发,最常用的就是在 Winfom、WPF 中,在 UI 线程中执行某些方法时,界面容易卡死,这时候就必须用线程,或者是 Task 才能解决卡顿。

关于多线程相关的理论知识,不只是我上面的写的那么简单,现在网上有大量的资料,有兴趣可以自己去查看。

二、线程的创建

对 Thread 实例化即可以开启一个新的线程,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(PrintNum); 
            t.Start();
            
            Console.ReadKey();
        }

        static void PrintNum()
        {
            Console.WriteLine("start....");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i);
            }
        }
    }
}

运行

在这里, PrintNum 方法执行完成后,线程在后面的 GC 回收中也会得到释放,这里演示的是一次性的线程执行方法。

上面用的常规的写法,使用 Lambda 表达式效果也是一样

Thread t = new Thread(() =>
{
    Console.WriteLine("start....");
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(i);
    }
});
t.Start();

三、线程的休眠

线程的休眠用 Thread.Sleep 方法,这个在平时的开发中也是用到的比较多的

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(PrintNumWithDelay); 
            t.Start();
            
            Console.ReadKey();
        }

        static void PrintNumWithDelay()
        {
            Console.WriteLine("start...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine(i);
            }
        }
    }
}

运行:

和上一节相比,这次执行起来会比较慢,每隔一秒才会执行一次。

四、线程的等待

线程的等待使用 Join 方法,它会等待线程内部的代码执行完成后,才会继续执行 Join 后面的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(PrintNumWithDelay); 
            t.Start();
            t.Join();
            Console.WriteLine("线程执行完成");

            Console.ReadKey();
        }

        static void PrintNumWithDelay()
        {
            Console.WriteLine("start...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine(i);
            }
        }
    }
}

运行:

五、线程的终止

线程的终止使用 Abort 方法,调用后会给线程执行的方法中抛出了一个 ThreadAbortException 异常,程序会因为异常而终止运行。其实这么做是非常不推荐的,因为有可能会导致应用程序的崩溃。另外,使用 Abort 方法不一定总能终止线程。目标线程可以通过处理该异常并调用 Thread.ResetAbort 方法来拒绝被终止。可优先使用一些其他方法,比如提供一个CancellationToken 方法来取消线程的执行。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(PrintNumWithDelay); 
            t.Start();
            Thread.Sleep(TimeSpan.FromSeconds(2));
            t.Abort();
            Console.WriteLine("线程已中止");

            Console.ReadKey();
        }

        static void PrintNumWithDelay()
        {
            Console.WriteLine("start...");
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(1));
                Console.WriteLine(i);
            }
        }
    }
}

运行:

六、线程的状态

获取线程的状态可以调用 ThreadState 属性来获取,下面代码演示了线程不同状态的输出效果,代码写的有点乱,可以不用去关注代码本身,重点是线程不同状态下形成的条件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Thread t1 = new Thread(PrintNumWithDelay);
            Thread t2 = new Thread(DoNothing);

            Console.WriteLine("t1.ThreadState:{0}", t1.ThreadState);

            t1.Start();
            t2.Start();

            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("t1.ThreadState:{0}", t1.ThreadState);
            }
            Thread.Sleep(TimeSpan.FromSeconds(6));
            t1.Abort();

            Console.WriteLine("t1.ThreadState:{0}", t1.ThreadState);
            Console.WriteLine("t2.ThreadState:{0}", t2.ThreadState);

            Console.ReadKey();
        }

        static void DoNothing()
        {
            Thread.Sleep(TimeSpan.FromSeconds(2));
        }

        static void PrintNumWithDelay()
        {
            Console.WriteLine("start...");
            Console.WriteLine("当前线程的状态:{0}", Thread.CurrentThread.ThreadState);
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(TimeSpan.FromSeconds(2));
                Console.WriteLine(i);
            }
        }
    }
}

下面的线程状态的枚举类型,来源于微软的源码

public enum ThreadState
{
    Running = 0x0,
    StopRequested = 0x1,
    SuspendRequested = 0x2,
    Background = 0x4,
    Unstarted = 0x8,
    Stopped = 0x10,
    WaitSleepJoin = 0x20,
    Suspended = 0x40,
    AbortRequested = 0x80,
    Aborted = 0x100
}

运行:

七、线程的优先级

线程优先级的设置使用 Priority 属性,Priority 属性的类型是为一个枚举类型,枚举名为 ThreadPriority ,ThreadPriority 优先级共有 5 个,如下:

public enum ThreadPriority
{
    Lowest,
    BelowNormal,
    Normal,
    AboveNormal,
    Highest
}

打开任务管理器 ---> 详细信息,也可以看到线程的优先级

下面用代码演示一下

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test5
{
    internal class Program
    {
        static bool IsStop = true;
        static int flag1 = 0;
        static int flag2 = 0;

        static void Main(string[] args)
        {
            Thread thread1 = new Thread(PrintCount1);
            Thread thread2 = new Thread(PrintCount2);
            thread1.Name = "thread1";
            thread2.Name = "thread2";
            thread1.Start();
            thread2.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));

            IsStop = false;

            Console.WriteLine("线程1执行次数:{0}", flag1);
            Console.WriteLine("线程2执行次数:{0}", flag2);

            Console.ReadKey();
        }

        static void PrintCount1()
        {
            while (IsStop)
            {
                flag1++;
            }
        }
      
        static void PrintCount2()
        {
            while (IsStop)
            {
                flag2++;
            }
        }
    }
}

运行第一次:

运行第二次:

运行第三次:

将代码加入一些优先级

namespace Test5
{
    internal class Program
    {
        static bool IsStop = true;
        static int flag1 = 0;
        static int flag2 = 0;

        static void Main(string[] args)
        {
            Thread thread1 = new Thread(PrintCount1);
            Thread thread2 = new Thread(PrintCount2);
            thread1.Name = "thread1";
            thread2.Name = "thread2";
            thread1.Priority = ThreadPriority.Highest;
            thread2.Priority = ThreadPriority.Lowest;
            thread1.Start();
            thread2.Start();

            Thread.Sleep(TimeSpan.FromSeconds(2));

            IsStop = false;

            Console.WriteLine("线程1执行次数:{0}", flag1);
            Console.WriteLine("线程2执行次数:{0}", flag2);

            Console.ReadKey();
        }

        static void PrintCount1()
        {
            while (IsStop)
            {
                flag1++;
            }
        }
      
        static void PrintCount2()
        {
            while (IsStop)
            {
                flag2++;
            }
        }
    }
}

第一次运行:

第二次运行:

第三次运行:

从图片上可以看到,这几次运行,数值要比之前不加优先级要大的多,另外,优先级高的线程,运行的数值,大部分时候都要高很多。

结束

如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言,谢谢!

end

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐