c#: 线程状态和管理之线程的休眠、挂起和中断

环境:

  • 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(结束线程最好的方法是通过信号灯让线程自己中断,而不是强制AbortC# 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);
        }
    }
}

输出效果:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章