环境:
- window 10
- .NetFramework 4.7
- vs2019 16.4.5
一、线程的状态
线程的状态可以从枚举ThreadState
中查看到:
public enum ThreadState
{
Running = 0x0,
StopRequested = 0x1,
SuspendRequested = 0x2,
Background = 0x4,
Unstarted = 0x8,
Stopped = 0x10,
WaitSleepJoin = 0x20,
Suspended = 0x40,
AbortRequested = 0x80,
Aborted = 0x100
}
我们先抛开Background
不谈,说一下其他的九种状态:
- Unstarted :
当我们var thread = new Thread(()=>{})执行后,这个线程就被创建了,此时它并不会被cpu执行,它的状态为:Unstarted - Running :
当我们调用thread.Start()方法后,这个线程就安排给cpu调用了,此时它的状态为Running - Stopped、StopRequested :
当我们创建的线程正常执行后,它没代码执行了,也就是死掉了,此时它的状态为Stopped ,注意:这里说的是正常执行完毕,并不是被强制终止(thead.Abort())。至于StopRequested,从名字可以看出它是处于Stopped状态之前的,可能是速度太快,我调试的时候并没有捕获到。最后,线程结束后不能重新发起 - WaitSleepJoin :
这种状态也称之为休眠状态,由thread.Join()或Thread.Sleep()引发,前者是主线程调用子线程的Join方法导致主线程阻塞(直到子线程执行完毕才能解除阻塞)并切换到WaitSleepJoin状态,后者是子线程内部调用Sleep方法导致子线程休眠并切换到WaitSleepJoin状态。从这个方法的名字中也可以看到它主要是由Join和Sleep方法引起的线程休眠(程序锁也会导致休眠),而解除这种状态的方法是thread.Interrupt()。注意:thread.Interrupt()会导致线程在阻塞处抛出异常,注意捕捉。 - Suspended、SuspendRequested:(挂起操作不被建议使用,标记为废弃的api,.netcore中不支持Suspend/Resume方法的调用)
线程被挂起时的状态,由thread.Suspend()引发。注意:进程的挂起和休眠不是一个意思,线程的休眠必须指定休眠的时间,时间一到就自动解除休眠,而挂起则不然,它必须被其他线程解除挂起才行,也可以这样说:线程的休眠是线程本身根据需要引发的,而挂起则是由管理者决定的。当一个线程处于WaitSleepJoin状态时管理者仍然可以将这个线程标记为挂起,只不过这个由于线程已经自己休眠了,所以就暂时安排个SuspendRequested状态,等它的休眠期已过就立刻打上Suspend状态。还有一个重要的地方,线程被挂起时会抛出异常,如果没有处理线程就会死掉,如果处理了,代码的执行位置就会改变,这一点和休眠是很不同的。最后,处于Suspend状态的线程只能由管理者调用Resume()方法解除挂起 - Aborted、AbortRequested(结束线程最好的方法是通过信号灯让线程自己中断,而不是强制Abort:C# Thread.Abort方法真的让线程停止了吗?,.netcore中不支持Abort方法的调用)
线程被终止状态,这个和Stopped不同,Stopped是正常执行完毕,而Aborted是被强制终止并抛出异常。
注意:
不要使用Suspend和Resume方法来同步线程的活动。当你Suspend线程时,您无法知道线程正在执行什么代码。如果在安全权限评估期间线程持有锁时挂起线程,则AppDomain中的其他线程可能会被阻塞。如果线程在执行类构造函数时Suspend,则试图使用该类的AppDomain中的其他线程将被阻塞。死锁很容易发生。
二、测试代码
2.1 测试Join()方法阻塞主线程
using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Threading;
namespace TestDI
{
class Program
{
public static void Main(string[] args)
{
Test6();
Console.WriteLine("ok");
Console.ReadLine();
}
private static void Test6()
{
var threadMain = Thread.CurrentThread;
var thread = new Thread(() =>
{
Thread.Sleep(2000);
for (int i = 0; i < 10000; i++)
{
File.AppendAllText("d:\\temp.txt", i + "\r\n");
}
//由于这个线程被主线程执行thread.Join(),从而导致主线程被阻塞,所以主线程状态为:WaitSleepJoin
Console.WriteLine("主线程状态:" + threadMain.ThreadState);
});
//线程thread刚被创建,还未启动,状态为:Unstarted
Console.WriteLine(thread.ThreadState);
thread.Start();
//线程thread启动后,线程的状态变为:Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(1000);
//线程thread此时执行到Thread.Sleep(2000);,状态为:WaitSleepJoin
Console.WriteLine(thread.ThreadState);
thread.Join();
//线程被执行了Join导致此处代码被阻塞,这里获取的状态肯定是Stopped
Console.WriteLine(thread.ThreadState);
//主线程状态也从WaitSleepJoin改为Running
Console.WriteLine("主线程:" + threadMain.ThreadState);
}
}
}
输出效果:
2.2 测试Abort()方法终止线程
.netcore不支持Abort,而且线程的正确终止方法应该是通过信号灯让线程本身结束,而不是强制结束,因为你无法预测线程正在做什么!
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Test5();
Console.WriteLine("ok");
Console.ReadLine();
}
private static void Test5()
{
var thread = new Thread(() =>
{
try
{
Thread.Sleep(2000);
}
catch (Exception ex)
{
Console.WriteLine("异常:" + ex.Message);
}
for (int i = 0; i < 10000; i++)
{
File.AppendAllText("d:\\temp.txt", i + "\r\n");
}
});
//线程thread刚被创建,还未启动,状态为:Unstarted
Console.WriteLine(thread.ThreadState);
thread.Start();
//线程thread启动后,线程的状态变为:Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(1000);
//线程thread此时执行了Thread.Sleep(2000);,状态为:WaitSleepJoin
Console.WriteLine(thread.ThreadState);
thread.Abort();
Thread.Sleep(500);
//线程thread已经Aborted了
Console.WriteLine(thread.ThreadState);
}
}
}
输出效果:
2.3 测试线程的挂起和休眠的叠加状态: 先从休眠中唤醒再解除挂起
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Test4();
Console.WriteLine("ok");
Console.ReadLine();
}
private static void Test4()
{
var thread = new Thread(() =>
{
try
{
Thread.Sleep(2000);
}
catch (Exception ex)
{
Console.WriteLine("异常:" + ex.Message);
}
for (int i = 0; i < 1000; i++)
{
File.AppendAllText("d:\\temp.txt", i + "\r\n");
}
});
//线程thread刚被创建,还未启动,状态为:Unstarted
Console.WriteLine(thread.ThreadState);
thread.Start();
//线程thread启动后,线程的状态变为:Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(1000);
//线程thread此时执行了Thread.Sleep(2000);,状态为:WaitSleepJoin
Console.WriteLine(thread.ThreadState);
thread.Suspend();
Thread.Sleep(500);
//线程thread被执行Suspend(),状态为SuspendRequested, WaitSleepJoin,并引发异常
Console.WriteLine(thread.ThreadState);
if (thread.ThreadState == (ThreadState.SuspendRequested | ThreadState.WaitSleepJoin))
{
Console.WriteLine("ThreadState.SuspendRequested | ThreadState.WaitSleepJoin\t" + true);
}
Thread.Sleep(1500);
//线程thread此时已从Sleep中苏醒,状态为Suspend
Console.WriteLine(thread.ThreadState);
thread.Resume();
Thread.Sleep(500);
//线程thread被执行Resume(),状态为Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(10000);
//线程thread已经执行完毕,状态为Stopped
Console.WriteLine(thread.ThreadState);
}
}
}
输出效果:
2.4 测试线程的挂起和休眠的叠加状态: 先解除挂起再从休眠中唤醒
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Test3();
Console.WriteLine("ok");
Console.ReadLine();
}
/// <summary>
/// 测试线程状态Unstarted->Running->WaitSleepJoin->SuspendRequested, WaitSleepJoin->WaitSleepJoin->Running->Stopped
/// 线程先睡眠再挂起,然后解除挂起,然后自动唤醒
/// .netcore不支持Suspend/Resume,而且这两个方法已经标记为废弃的api
/// </summary>
private static void Test3()
{
var thread = new Thread(() =>
{
try
{
Thread.Sleep(2000);
}
catch (Exception ex)
{
Console.WriteLine("异常:" + ex.Message);
}
for (int i = 0; i < 1000; i++)
{
File.AppendAllText("d:\\temp.txt", i + "\r\n");
}
});
//线程thread刚被创建,还未启动,状态为:Unstarted
Console.WriteLine(thread.ThreadState);
thread.Start();
//线程thread启动后,线程的状态变为:Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(1000);
//线程thread此时执行了Thread.Sleep(2000);,状态为:WaitSleepJoin
Console.WriteLine(thread.ThreadState);
thread.Suspend();
Thread.Sleep(500);
//线程thread被执行Suspend(),状态为SuspendRequested, WaitSleepJoin
Console.WriteLine(thread.ThreadState);
thread.Resume();
Thread.Sleep(500);
//线程thread被执行Resume(),状态为WaitSleepJoin
Console.WriteLine(thread.ThreadState);
Thread.Sleep(2000);
//线程thread已经从Sleep中苏醒,状态为Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(8000);
//线程thread已经执行完毕,状态为Stopped
Console.WriteLine(thread.ThreadState);
}
}
}
输出效果:
2.5 测试线程休眠后使用Interrupt方法唤醒
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Test2();
Console.WriteLine("ok");
Console.ReadLine();
}
/// <summary>
/// 测试线程状态Unstarted->Running->WaitSleepJoin->Running->Stopped
/// 休眠后使用Interrupt方法唤醒
/// </summary>
private static void Test2()
{
var thread = new Thread(() =>
{
try
{
Thread.Sleep(2000);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
for (int i = 0; i < 1000; i++)
{
File.AppendAllText("d:\\temp.txt", i + "\r\n");
}
});
//线程thread刚被创建,还未启动,状态为:Unstarted
Console.WriteLine(thread.ThreadState);
thread.Start();
//线程thread启动后,线程的状态变为:Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(1000);
//线程thread此时执行了Thread.Sleep(2000);,状态为:WaitSleepJoin
Console.WriteLine(thread.ThreadState);
thread.Interrupt();
Thread.Sleep(100);
//线程thread被执行Interrupt(),状态为Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(10 * 1000);
//线程thread已经执行完毕,状态为Stopped
Console.WriteLine(thread.ThreadState);
}
}
}
输出效果:
2.6 测试线程休眠后自动唤醒
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Test1();
Console.WriteLine("ok");
Console.ReadLine();
}
/// <summary>
/// 测试线程状态Unstarted->Running->WaitSleepJoin->Running->Stopped
/// 休眠到期后自动唤醒
/// </summary>
private static void Test1()
{
var thread = new Thread(() =>
{
Thread.Sleep(2000);
for (int i = 0; i < 1000; i++)
{
File.AppendAllText("d:\\temp.txt", i + "\r\n");
}
});
//线程thread刚被创建,还未启动,状态为:Unstarted
Console.WriteLine(thread.ThreadState);
thread.Start();
//线程thread启动后,线程的状态变为:Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(1000);
//线程thread此时执行了Thread.Sleep(2000);,状态为:WaitSleepJoin
Console.WriteLine(thread.ThreadState);
Thread.Sleep(1500);
//线程thread此时正在运行,状态为Running
Console.WriteLine(thread.ThreadState);
Thread.Sleep(8000);
//线程thread此时已经运行完毕,状态为Stopped
Console.WriteLine(thread.ThreadState);
}
}
}
输出效果: