代码改变世界

C# 线程手册 第二章 .NET 中的线程 线程的生命周期

2012-01-15 21:51  DanielWise  阅读(7815)  评论(22编辑  收藏  举报

当一个线程计划执行时它可以经过几个状态,包括未开始,活跃,睡眠,等等。线程类包含几个允许你启动、停止、恢复、退出、暂停以及等待一个线程的方法。我们可以使用ThreadState属性来获取线程的当前状态,状态值可能是ThreadState枚举中的一个:

Aborted - 线程当前处理停止状态,但是不一定已经执行完。

AbortRequested – 已经调用Abort() 方法但是线程还没有接收到将试图终止线程的System.Threading.ThreadAbortexception。虽然线程还没有停止,但是马上就会。

Background - 线程在后台执行。

Running - 线程已经启动而且没有被阻塞。

Stopped - 线程已经完成了所有指令并停止了。

StopRequested - 请求线程停止。

Suspend - 线程已经被挂起。

SuspendRequested - 请求挂起线程。

Unstarted - 线程还没有调用Start()方法之前。

WaitSleepJoin - 调用Wait(), Sleep() 或 Join() 方法后处理阻塞状态的线程。

图3 显示了一个线程的生命周期。

图3

在这部分,我们将讨论线程的生命周期

 

让一个线程进入睡眠状态

当我们创建一个线程后,我们需要调用线程对象的Start()方法来调度那个线程。在这时,CLR将会为作为构造函数参数传递给线程对象的方法地址分配一个时间片。一旦线程开始执行,它就可以在操作系统处理其他线程时回到睡眠状态或者退出状态。我们可以使用线程类的Sleep()方法让一个线程进入睡眠状态。如果你正在等待一个资源并且你想在稍后继续尝试访问这个资源时,Sleep()方法是很重要的。举个例子,假设你的程序由于无法访问需要的资源而导致其不能继续执行时,你可能想要在几毫秒之后尝试继续访问资源,在这种情况下让线程在再次尝试访问资源之前睡眠一段时间是一个很好的方式。

Sleep()方法有两种重载方式。第一种重载方法有一个整型参数,并会按照指定的毫秒时间暂停线程执行。例如,如果你向线程传递值100,那么线程将会暂停100毫秒。这个方法将会让线程进入WaitSleepJoin状态。让我们看一个例子,thread_sleep2.cs:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:Daniel Dong
 * Blog:  www.cnblogs.com/danielWise
 * Email: guofoo@163.com
 * 
 */

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

namespace SimpleThread
{
    public class ThreadSleep
    {
        public static Thread worker;
        public static Thread worker2;

        public static void Main()
        {
            Console.WriteLine("Entering the void Main!");

            worker = new Thread(new ThreadStart(Counter));
            worker2 = new Thread(new ThreadStart(Counter2));

            //Make the worker2 Object as highest priority
            worker2.Priority = ThreadPriority.Highest;

            worker.Start();
            worker2.Start();

            Console.WriteLine("Exiting the void Main!");
            Console.ReadLine();
        }

        public static void Counter()
        {
            Console.WriteLine("Entering Counter");
            for (int i = 1; i < 50; i++)
            {
                Console.Write(i + " ");
                if (i == 10)
                {
                    Console.WriteLine();
                    Thread.Sleep(1000);
                }
            }
            Console.WriteLine("Exiting Counter");
        }

        public static void Counter2()
        {
            Console.WriteLine("Entering Counter2");
            for (int i = 51; i < 100; i++)
            {
                Console.Write(i + " ");
                if (i == 70)
                {
                    Console.WriteLine();
                    Thread.Sleep(5000);
                }
            }
            Console.WriteLine("Exiting Counter2");
        }
    }
}

Counter()方法从1到50计数,当到达10以后睡眠1000毫秒。Counter2()方法从51~100计数,当到达70时睡眠5000毫秒。下面是输出结果:

image

注:以上是在多核CPU下运行的结果,单核CPU 执行情况可能与上图有所出入。

 

第二种重载方法有一个TimeSpan类型参数,当前线程会按照TimeSpan的值暂停一段时间。TimeSpan是System命名空间中的一个类。TimeSpan有一些很有用的属性并会返回基于时钟时间间隔。

我们可以使用FromSeconds()和FromMinutes()来确定睡眠时间。下面是一个例子,thread_sleep3.cs:

public static void Counter()
{
    Console.WriteLine("Entering Counter");
    for (int i = 1; i < 50; i++)
    {
        Console.Write(i + " ");
        if (i == 10)
        {
            Console.WriteLine();
            Thread.Sleep(TimeSpan.FromSeconds(1));
        }
    }
    Console.WriteLine("Exiting Counter");
}

public static void Counter2()
{
    Console.WriteLine("Entering Counter2");
    for (int i = 51; i < 100; i++)
    {
        Console.Write(i + " ");
        if (i == 70)
        {
            Console.WriteLine();
            Thread.Sleep(TimeSpan.FromSeconds(5));
        }
    }
    Console.WriteLine("Exiting Counter2");
}

输出结果与thread_sleep2类似。

 

中断一个线程

当让一个线程睡眠时,它实际会进入WaitSleepJoin状态。如果线程处理睡眠状态,那么在它超时退出之前唯一可以唤醒线程的方式是使用Interrupt()方法。Interrupt()方法将让线程回到调度队列中去。让我们看一个例子,thread_interrupt.cs:

/*************************************
/* Copyright (c) 2012 Daniel Dong
 * 
 * Author:Daniel Dong
 * Blog:  www.cnblogs.com/danielWise
 * Email: guofoo@163.com
 * 
 */

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

namespace SimpleThread
{
    public class Interrupt
    {
        public static Thread sleeper;
        public static Thread worker;

        public static void Main()
        {
            Console.WriteLine("Entering the void Main!");

            sleeper = new Thread(new ThreadStart(SleepingThread));
            worker = new Thread(new ThreadStart(AwakeThread));

            sleeper.Start();
            worker.Start();

            Console.WriteLine("Exiting the void Main!");
            Console.ReadLine();
        }
        public static void SleepingThread()
        {
            for (int i = 1; i < 50; i++)
            {
                Console.Write(i + " ");
                if (i == 10 || i == 20 || i == 30)
                {
                    Console.WriteLine("Going to sleep at: " + i);
                    try
                    {
                        Thread.Sleep(20);
                    }
                    catch (ThreadInterruptedException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }

        public static void AwakeThread()
        {
            for (int i = 51; i < 100; i++)
            {
                Console.Write(i + " ");
                if (sleeper.ThreadState == ThreadState.WaitSleepJoin)
                {
                    Console.WriteLine("Interrupting the sleeping thread.");
                    sleeper.Interrupt();
                }
            }
        }
    }
}

在上面的例子中,当计数器的值为10, 20 和 30 时第一个线程会睡眠。第二个线程会检查第一个线程是否已经进入睡眠状态。如果是的话,它将中断第一个线程并使它回到调度队列中去。Interrupt()方法是让睡眠线程重新醒来的最好方式,当线程等待的资源可用且你想让线程继续运行时你可以使用这个方法。输出结果与下面显示的类似:

E(QX(]YAP9AJY~P[[(Z7V21

下一篇将会继续介绍中断和恢复线程…