目录
一、概述
二、线程的创建
三、线程的休眠
四、线程的等待
五、线程的终止
六、线程的状态
七、线程的优先级
一、概述
线程(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