一、启动线程
通过提供一个委托来启动线程,该委托表示线程在其类构造函数中执行的方法。 然后调用 Start 方法以开始执行。
构造函数可以使用两种委托类型之一,具体取决于是否可以将参数 Thread 传递给要执行的方法:
如果方法没有参数,则向 ThreadStart 构造函数传递委托。 它具有签名:
public delegate void ThreadStart()
如果方法具有参数,则向 ParameterizedThreadStart 构造函数传递委托。 它具有签名:
public delegate void ParameterizedThreadStart(object obj)
在C#中创建线程时,首先需要创建一个Thread委托实例,再以这个TreadStart委托作为参数,来构造Thread实例。
二、Thread 构造函数
Thread类拥有四种重载的构造函数:
Thread(ParameterizedThreadStart) | 初始化 Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托。 |
Thread(ThreadStart) | 初始化 Thread 类的新实例。 |
Thread(ParameterizedThreadStart, Int32) | 初始化 Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托,并指定线程的最大堆栈大小。 |
Thread(ThreadStart, Int32) | 初始化 Thread 类的新实例,指定线程的最大堆栈大小。 |
注意:线程不会在创建时开始执行。 若要计划线程的执行,请调用 Start 方法。 若要将数据对象传递给线程,请使用 Start(Object) 方法重载。
备注:如果线程已经终止,则无法通过再次调用Start方法来重启线程。
1、Thread(ParameterizedThreadStart)
初始化 Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托。
//未使用命名空间
public Thread (System.Threading.ParameterizedThreadStart start);
//使用了命名空间
using System.Threading;
public Thread (ParameterizedThreadStart start);
using System;
using System.Threading;
public class Work
{
public static void Main()
{
// 启动一个调用参数化静态方法的线程。
Thread newThread = new Thread(Work.DoWork);
newThread.Start(42);
// 启动一个调用参数化实例方法的线程。
Work w = new Work();
newThread = new Thread(w.DoMoreWork);
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(ThreadStart)
//未使用命名空间
public Thread (System.Threading.ThreadStart start);
//使用了命名空间
using System.Threading;
public Thread (ThreadStart start);
创建执行静态方法的线程:
using System;
using System.Threading;
class Test
{
static void Main()
{
Thread Nt1 =new Thread(new ThreadStart(Work.DoWork));
Nt1.Start();
}
}
class Work
{
public static void DoWork()
{
Console.WriteLine("新的线程启动(静态方法)");
}
}
创建执行实例方法的线程:
using System;
using System.Threading;
class Test
{
static void Main()
{
Work Nw1 = new Work();
Thread Nt1 =new Thread(new ThreadStart(Nw1.DoWork));
Nt1.Start();
}
}
class Work
{
public void DoWork()
{
Console.WriteLine("新的线程启动(实例方法)");
}
}
找错误案例:
正确修改为:
using System;
using System.Threading;
class Test
{
static void Main()
{
Test x1 = new Test();
Thread x2 = new Thread(new ThreadStart(x1.DoWork));
x2.Start();
}
public void DoWork()
{
Console.WriteLine("新的线程启动(实例方法)");
}
}
3、Thread(ParameterizedThreadStart, Int32)
4、Thread(ThreadStart, Int32)
三、线程休眠—Thread.Sleep 方法:将当前线程挂起指定的时间。
线程的休眠是通过Thread类的Sleep方法实现的,而Thread类的实例的IsAlive属性可以判断线程是否执行完毕。
Sleep方法有两种重载方式:
Sleep(Int32) | 将当前线程挂起指定的毫秒数。 |
Sleep(TimeSpan) | 将当前线程挂起指定的时间。 |
注意:不要在主线程使用!
1、Sleep(Int32):将当前线程挂起指定毫秒数,C#语法格式如下:
public static void Sleep (int millisecondsTimeout);
millisecondsTimeout:Int32,挂起线程的毫秒数。 如果 millisecondsTimeout
参数的值为零,则该线程会将其时间片的剩余部分让给任何已经准备好运行的、具有同等优先级的线程。 如果没有其他已经准备好运行的、具有同等优先级的线程,则不会挂起当前线程的执行。
using System;
using System.Threading;
class Example
{
static void Main()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("休眠 2 秒.");
Thread.Sleep(2000);
}
Console.WriteLine("Main thread exits.");
}
}
2、Sleep(TimeSpan):将当前线程挂起指定的时间,语法格式如下:
public static void Sleep (TimeSpan timeout);
timeout:挂起线程的时间量。 如果 timeout
参数的值为零,则该线程会将其时间片的剩余部分让给任何已经准备好运行的、具有同等优先级的线程。 如果没有其他已经准备好运行的、具有同等优先级的线程,则不会挂起当前线程的执行。
将当前线程挂起指定的时间:多少小时,多少分钟,多少秒
public TimeSpan(int hours, int minutes, int seconds);
using System;
using System.Threading;
class Example
{
static void Main()
{
TimeSpan interval = new TimeSpan(0, 0, 2);//0小时0分钟2秒
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Sleep for 2 seconds.");
Thread.Sleep(interval);
}
Console.WriteLine("Main thread exits.");
}
}
四、终止线程—Abort方法
当一个线程执行时间太长时,用户有可能要终止这个线程,这个就要使用Abort方法。该方法有两种重载方式:
Abort() | 在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程。 |
Abort(Object) | 引发在其上调用的线程中的 ThreadAbortException 以开始处理终止线程,同时提供有关线程终止的异常信息。 调用此方法通常会终止线程。 |
备注:线程的Abort方法用于永久地停止托管线程。在调用Abort方法时,公共语言运行库在目标线程中引发ThreadAbortException异常,目标线程可捕捉此异常。一旦线程终止,将无法重新启动。
在调用此方法的线程上引发 ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程。
public void Abort ();
引发在其上调用的线程中的 ThreadAbortException 以开始处理终止线程,同时提供有关线程终止的异常信息。 调用此方法通常会终止线程。
public void Abort(Object stateInfo)
stateInfo:一个对象,它包含应用程序特定的信息(如状态),该信息可供正被中止的线程使用。
using System;
using System.Threading;
class Test
{
public static void Main()
{
Thread newThread = new Thread(new ThreadStart(TestMethod));
newThread.Start();
Thread.Sleep(1000);
// Abort newThread.
Console.WriteLine("Main aborting new thread.");
newThread.Abort("Information from Main.");
// Wait for the thread to terminate.
newThread.Join();
Console.WriteLine("New thread terminated - Main exiting.");
}
static void TestMethod()
{
try
{
while (true)
{
Console.WriteLine("New thread running.");
Thread.Sleep(1000);
}
}
catch (ThreadAbortException abortException)
{
Console.WriteLine((string)abortException.ExceptionState);
}
}
}
五、线程的加入 —Join方法:在此实例表示的线程终止前,阻止调用线程。
Join方法用来阻塞调用线程,直到某个线程终止为止。该方法有三种重载形式:
Join() | 在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止。 |
Join(Int32) | 在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止或经过了指定时间为止。 |
Join(TimeSpan) | 在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止或经过了指定时间为止。 |
备注:如果在程序中使用了多线程,辅助线程还没有执行完毕,那么在关闭窗体时,必须要关闭辅助线程,否则会引发异常。
① Join()
在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止。
public void Join ();
Join 是一种同步方法,它阻止调用 (线程,即调用方法的线程) 调用方法的线程 Join 完成。
使用此方法可确保线程已终止。
如果线程未终止,调用方将无限期阻止。
在下面的示例中,线程调用 的 方法 Thread1 Join() Thread2 ,这会导致 Thread1 在 Thread2 完成后阻止。
using System;
using System.Threading;
public class Example
{
static Thread thread1, thread2;
public static void Main()
{
thread1 = new Thread(ThreadProc);
thread1.Name = "Thread1";
thread1.Start();
thread2 = new Thread(ThreadProc);
thread2.Name = "Thread2";
thread2.Start();
}
private static void ThreadProc()
{
Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
if (Thread.CurrentThread.Name == "Thread1" &&
thread2.ThreadState != ThreadState.Unstarted)
thread2.Join();
Thread.Sleep(4000);
Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
Console.WriteLine("Thread1: {0}", thread1.ThreadState);
Console.WriteLine("Thread2: {0}\n", thread2.ThreadState);
}
}
时线程已终止 Join ,则该方法将立即返回 。
②Join(Int32)
在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止或经过了指定时间为止。
public bool Join (int millisecondsTimeout);
参数:millisecondsTimeout—等待线程终止的毫秒数。
如果线程已终止,则为 true;如果 false 参数指定的时间量已过之后还未终止线程,则为 millisecondsTimeout。
using System;
using System.Threading;
public class Example
{
static Thread thread1, thread2;
public static void Main()
{
thread1 = new Thread(ThreadProc);
thread1.Name = "Thread1";
thread1.Start();
thread2 = new Thread(ThreadProc);
thread2.Name = "Thread2";
thread2.Start();
}
private static void ThreadProc()
{
Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
if (Thread.CurrentThread.Name == "Thread1" &&
thread2.ThreadState != ThreadState.Unstarted)
if (thread2.Join(2000))
Console.WriteLine("Thread2 has termminated.");
else
Console.WriteLine("The timeout has elapsed and Thread1 will resume.");
Thread.Sleep(4000);
Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
Console.WriteLine("Thread1: {0}", thread1.ThreadState);
Console.WriteLine("Thread2: {0}\n", thread2.ThreadState);
}
}
③Join(TimeSpan)
在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻止调用线程,直到由该实例表示的线程终止或经过了指定时间为止。
public bool Join (TimeSpan timeout);
timeout:设置等待线程终止的时间量的TimeSpan。
如果线程已终止,则为 true;如果 false 参数指定的时间量已过之后还未终止线程,则为 timeout。
//下面的代码示例演示如何将 值 TimeSpan 与 方法 Join 一起使用。
using System;
using System.Threading;
class Test
{
static TimeSpan waitTime = new TimeSpan(0, 0, 1);
public static void Main()
{
Thread newThread = new Thread(Work);
newThread.Start();
if(newThread.Join(waitTime + waitTime)) {
Console.WriteLine("New thread terminated.");
}
else {
Console.WriteLine("Join timed out.");
}
}
static void Work()
{
Thread.Sleep(waitTime);
}
}
备注:Join(TimeSpan) 是一种同步方法,它阻止调用线程 (,即调用方法) 直到调用方法的线程已完成或已过时间间隔 Join 。
//下面的示例中,线程调用 的 方法,这会导致 阻塞,直到已完成或 Thread1 Join() Thread2 Thread1 Thread2 2 秒已过。
using System;
using System.Threading;
public class Example
{
static Thread thread1, thread2;
public static void Main()
{
thread1 = new Thread(ThreadProc);
thread1.Name = "Thread1";
thread1.Start();
thread2 = new Thread(ThreadProc);
thread2.Name = "Thread2";
thread2.Start();
}
private static void ThreadProc()
{
Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
if (Thread.CurrentThread.Name == "Thread1" &&
thread2.ThreadState != ThreadState.Unstarted)
if (thread2.Join(TimeSpan.FromSeconds(2)))
Console.WriteLine("Thread2 has termminated.");
else
Console.WriteLine("The timeout has elapsed and Thread1 will resume.");
Thread.Sleep(4000);
Console.WriteLine("\nCurrent thread: {0}", Thread.CurrentThread.Name);
Console.WriteLine("Thread1: {0}", thread1.ThreadState);
Console.WriteLine("Thread2: {0}\n", thread2.ThreadState);
}
}
如果 Timeout.Infinite 为 指定了 ,则此方法的行为与方法重载相同, timeout
返回值 Join() 除外。
如果调用 时线程已终止 Join ,则该方法将立即返回 。
此方法更改当前线程的状态以包括 WaitSleepJoin 。 不能对 Join
状态为 的线程 ThreadState.Unstarted 调用 。
六、线程的挂起与恢复(新版本已弃用)
Suspend方法用于挂起线程,Resume方法用于继续执行已经挂起的线程。可以使用这两个方法进行线程的同步,和Start方法有些类似的是:在调用Suspend方法后不会立即停止,而是执行到一个安全点后挂起。
1、Suspend方法
挂起线程,或者如果线程已挂起,则不起作用。语法格式为:
public void Suspend();
2、Resume方法
继续已挂起的线程。语法格式为:
public void Resumne();