1. C#多线程技术简介
线程是一个独立的运行单元,每个进程内部有多个线程,每个线程可以各自同时执行指令。每个线程有自己独立的栈,但是与进程内的其他线程共享内存。对某些程序来说,其中有一个线程是特殊的,例如:Console的main
线程和窗体程序的UI
线程。
每个.NET
程序都有一个线程池,线程池维护着一定数量的工作线程,这些线程等待着执行分配下来的任务,线程池可以随时监测线程的数量。配置线程池的参数很多。但是我都建议大家使用默认值,这些值都是经过微软调试好的,可以满足大部分应用。
线程是低级别的抽象,线程池虽然高级一点,但同样很低,而现在C#给我们提供了很多高级的并发编程技术工具,所以原则上我们不建议直接操作
Thread
对象。但是为了让大家很好的理解C#多线程的来龙去脉,这里介绍C#最初操作多线程的方法。
2 实战:第一个程序
由于写博客我喜欢用Mac
,所以我的例程是用.net core
框架,使用 VS Code
开发,源码发布到github中,链接:spartajet/CSharpLearnBlog
2.1 C# 创建线程
首先,我们要创建一个新建线程中运行的方法,主要是打印数字,如下:
/// <summary>
/// 数数方法
/// </summary>
static void NumberCount(){
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"The number is {i}");
}
}
在 main
方法中添加如下代码来开启一个线程
static void Main(string[] args)
{
Thread workThread=new Thread(NumberCount);
workThread.Start();
NumberCount();
}
运行结果,如下:
The number is 0
The number is 0
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
The number is 6
The number is 7
The number is 8
The number is 9
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
The number is 6
The number is 7
The number is 8
The number is 9
可以看出,C#的多线程执行顺序是不确定的。NumberCount
方法同时被工作线程和主线程调用,可以看到工作线程和主线程是同步运行的,互不干扰。
Thread
的声明还有其他方式,可以使用Lambda
风格来定义。方法如下:
var workThread=new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"The number is {i}");
}
});
2.2 暂停一个线程
暂停一个线程是让一个线程等待一段时间而不消耗操作系统资源。
将方法NumberCount
修改为:
/// <summary>
/// 数数方法
/// </summary>
static void NumberCountCouldDelay(){
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"The number is {i}");
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
运行结果为:
The number is 0
The number is 0
The number is 1
The number is 1
The number is 2
The number is 2
The number is 3
The number is 3
The number is 4
The number is 4
The number is 5
The number is 5
The number is 6
The number is 6
The number is 7
The number is 7
The number is 8
The number is 8
The number is 9
The number is 9
大家可以看到,方法改动只是增加了一行
Thread.Sleep(TimeSpan.FromSeconds(1));
Thread.Sleep
方法被调用后,线程处于休眠状态,会尽可能的少占用系统资源,起到了暂停线程的效果。
2.3 线程等待
线程等待是指多线程编程中,一个线程等待另一个线程完成后再执行。
代码如下:
static void Main(string[] args)
{
var workThread=new Thread(NumberCountCoudDelay);
workThread.Start();
workThread.Join();
NumberCount();
}
/// <summary>
/// 数数方法
/// </summary>
static void NumberCountCoudDelay(){
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Delay thread number is {i}");
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
/// <summary>
/// 数数方法
/// </summary>
static void NumberCount(){
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"Main thread number is {i}");
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
运行结果:
Delay thread number is 0
Delay thread number is 1
Delay thread number is 2
Delay thread number is 3
Delay thread number is 4
Delay thread number is 5
Delay thread number is 6
Delay thread number is 7
Delay thread number is 8
Delay thread number is 9
Main thread number is 0
Main thread number is 1
Main thread number is 2
Main thread number is 3
Main thread number is 4
Main thread number is 5
Main thread number is 6
Main thread number is 7
Main thread number is 8
Main thread number is 9
可以看出来,我们使用了 Join
方法,该方法允许我们等待知道此线程完成,当线程完成后,下面的代码才可以执行。
2.4 结束线程
结束线程是指在某个线程运行中间停止该线程。
代码如下:
static void Main(string[] args)
{
var workThread=new Thread(NumberCountCoudDelay);
workThread.Start();
Thread.Sleep(TimeSpan.FromSeconds(4));
workThread.Abort();
Console.WriteLine("Work thread is stopped!!!");
}
结果如下:
Delay thread number is 0
Delay thread number is 1
Delay thread number is 2
Delay thread number is 3
Unhandled Exception: Delay thread number is 4
System.PlatformNotSupportedException: Thread abort is not supported on this platform.
at System.Threading.Thread.Abort()
at CSharpAsync.Program.Main(String[] args) in /Users/spartajet/CodeWorkSpace/VSCode/CSharpLearnBlog/CSharpAsync/Program.cs:line 12
为什么会出现这个现象呢,请参照:Methods that throw PlatformNotSupportedException are not documented
可以看出.net core
对 Thread
的支持不够,是因为 Thread
已经完全落伍了,同样在Windows
我们也不建议这么做。
方法代码如下:
public void Abort()
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort);
}
public void Abort(object stateInfo)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort);
}
public static void ResetAbort()
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadAbort);
}
[Obsolete("Thread.Suspend has been deprecated. Please use other classes in System.Threading, such as Monitor, Mutex, Event, and Semaphore, to synchronize Threads or protect resources. http://go.microsoft.com/fwlink/?linkid=14202", false)]
public void Suspend()
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ThreadSuspend);
}
但是在Windows
平台这段代码是完全可行的。
2.5 检测线程状态
一个线程可以用 ThreadState
枚举来表示,
下面是ThreadState
的源码:
public enum ThreadState
{
Running = 0,
StopRequested = 1,
SuspendRequested = 2,
Background = 4,
Unstarted = 8,
Stopped = 16, // 0x00000010
WaitSleepJoin = 32, // 0x00000020
Suspended = 64, // 0x00000040
AbortRequested = 128, // 0x00000080
Aborted = 256, // 0x00000100
}
测试代码如下:
static void Main(string[] args)
{
var workThread=new Thread(NumberCountCoudDelay);
Console.WriteLine($"work thread state: {workThread.ThreadState}");
workThread.Start();
workThread.Join();
Console.WriteLine($"work thread state: {workThread.ThreadState}");
Console.WriteLine("Work thread is stopped!!!");
}
结果如下:
work thread state: Unstarted
Delay thread number is 0
work thread state: Running
Delay thread number is 1
work thread state: Running
Delay thread number is 2
work thread state: Running
Delay thread number is 3
work thread state: Running
Delay thread number is 4
work thread state: Running
Delay thread number is 5
work thread state: Running
Delay thread number is 6
work thread state: Running
Delay thread number is 7
work thread state: Running
Delay thread number is 8
work thread state: Running
Delay thread number is 9
work thread state: Running
work thread state: Stopped
Work thread is stopped!!!