.NET将关于多线程的功能定义在System.Threading名称空间中,因此,如果您的程序要使用多线程,必须引用此命名空间(using System.Threading)。 我们知道,在.NET中使用多线程有两种方式:
1,使用Thread创建一个新的线程。
2,使用ThreadPool。
首先我们先说说和Thread有关的几个概念。
1. 创建线程和启动线程,如果代码可实现
Thread newThread = new Thread(new ThreadStart(Work.DoWork));
newThread.Start();
或者
Thread newThread = new Thread(new ParameterizedThreadStart(Work.DoWork));
newThread.Start(42);
ParameterizedThreadStart 此委托在 .NET Framework 2.0 版中是新增的。在创建托管的线程时,在该线程上执行的方法将通过一个传递给 Thread 构造函数的 ThreadStart 委托或 ParameterizedThreadStart 委托来表示。在调用 System.Threading.Thread.Start 方法之前,该线程不会开始执行。执行将从 ThreadStart 或 ParameterizedThreadStart 委托表示的方法的第一行开始。ParameterizedThreadStart 委托和 Thread.Start(Object) 方法重载使得将数据传递给线程过程变得简单,但由于可以将任何对象传递给 Thread.Start(Object),因此这种方法并不是类型安全的。将数据传递给线程过程的一个更可靠的方法是将线程过程和数据字段都放入辅助对象。
下面的代码示例演示通过静态方法和实例方法创建和使用 ParameterizedThreadStart 委托的语法,ThreadStart委托的使用方法和ParameterizedThreadStart一样,唯一的区别在ThreadStart封装的方法不需要参数。
using System;
using System.Threading;
public class Work
{
public static void Main()
{
// To start a thread using a shared thread procedure, use
// the class name and method name when you create the
// ParameterizedThreadStart delegate.
//
Thread newThread = new Thread(
new ParameterizedThreadStart(Work.DoWork));
// Use the overload of the Start method that has a
// parameter of type Object. You can create an object that
// contains several pieces of data, or you can pass any
// reference type or value type. The following code passes
// the integer value 42.
//
newThread.Start(42);
// To start a thread using an instance method for the thread
// procedure, use the instance variable and method name when
// you create the ParameterizedThreadStart delegate.
//
Work w = new Work();
newThread = new Thread(
new ParameterizedThreadStart(w.DoMoreWork));
// Pass an object containing data for the thread.
newThread.Start("The answer.");
}
public static void DoWork(object data)
{
Console.WriteLine("Static thread procedure. Data='{0}'",data);
}
public void DoMoreWork(object data)
{
Console.WriteLine("Instance thread procedure. Data='{0}'",data);
}
}
2. 挂起线程
挂起线程分为两种,主动挂起和被动挂起。
主动挂起可表示为:Thread.Sleep (Int32) 或 Thread.Sleep (TimeSpan) ,表示将当前调用线程挂起指定的时间。被动挂起表示为:Thread newThread = new Thread(new ThreadStart(Work.DoWork));
newThread.Start();
newThread.Join();
或者
Thread.CurrentThread.Join(50);
Join表示在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到某个线程终止为止。在[STAThread]指示的Com线程模型中,我们应该使用Thread.CurrentThread.Join(50)这种方式。有一个与之有关的示例,
Thread.Sleep vs. Thread.CurrentThread.Join
3、终止线程
在调用 Abort 方法以销毁线程时,公共语言运行库将引发 ThreadAbortException。ThreadAbortException 是一种可捕获的特殊异常,但在 catch 块的结尾处它将自动被再次引发。引发此异常时,运行库将在结束线程前执行所有 finally 块。由于线程可以在 finally 块中执行未绑定计算,或调用 Thread.ResetAbort 来取消中止,所以不能保证线程将完全结束。如果您希望一直等到被中止的线程结束,可以调用 Thread.Join 方法。Join 是一个模块化调用,它直到线程实际停止执行时才返回。
注意:在托管可执行文件中的所有前台线程已经结束后,当公共语言运行库 (CLR) 停止后台线程时,它不使用 System.Threading.Thread.Abort。因此,无法使用 ThreadAbortException 来检测 CLR 何时终止后台线程。
下面的示例说明如何中止线程。接收 ThreadAbortException 的线程使用 ResetAbort 方法取消中止请求并继续执行。
using System;
using System.Threading;
using System.Security.Permissions;
public class ThreadWork
{
public static void DoWork()
{
try
{
for(int i=0; i<100; i++)
{
Console.WriteLine("Thread - working.");
Thread.Sleep(100);
}
}
catch(ThreadAbortException e)
{
Console.WriteLine("Thread - caught ThreadAbortException - resetting.");
Console.WriteLine("Exception message: {0}", e.Message);
Thread.ResetAbort();
}
Console.WriteLine("Thread - still alive and working.");
Thread.Sleep(1000);
Console.WriteLine("Thread - finished working.");
}
}
class ThreadAbortTest
{
public static void Main()
{
ThreadStart myThreadDelegate = new ThreadStart(ThreadWork.DoWork);
Thread myThread = new Thread(myThreadDelegate);
myThread.Start();
Thread.Sleep(100);
Console.WriteLine("Main - aborting my thread.");
myThread.Abort();
myThread.Join();
Console.WriteLine("Main ending.");
}
}
这段代码产生以下输出:
Thread - working.
Main - aborting my thread.
Thread - caught ThreadAbortException - resetting.
Exception message: Thread was being aborted.
Thread - still alive and working.
Thread - finished working.
Main ending.
最后还有一点需要说明的是,在.NET Framework 2.0后,Thread对象的Suspend和Resume方法已被摈弃。原因在于使用 Suspend 和 Resume 方法来同步线程的活动。您无法知道挂起线程时它正在执行什么代码。如果您在安全权限评估期间挂起持有锁的线程,则 AppDomain 中的其他线程可能被阻止。如果您在线程正在执行类构造函数时挂起它,则 AppDomain 中试图使用该类的其他线程将被阻止。很容易发生死锁。