一、线程的创建
1、多线程执行无参数的方法
线程用Thread类来创建, 通过ThreadStart委托来指明方法从哪里开始运行,调用Start方法后,线程开始运行,线程一直到它所调用的方法返回后结束。
class Program { static void Main(string[] args) { Thread t = new Thread(new ThreadStart(Go)); t.Start(); } public static void Go() { Console.WriteLine("GO"); } }
当然也可以不需要ThreadStart委托更便利地创建出来:在这种情况,ThreadStart被编译器自动推断出来。
tatic void Main(string[] args) { Thread t = new Thread(Go); t.Start(); } public static void Go() { Console.WriteLine("GO"); }
另一个快捷的方式是使用匿名方法来启动线程:
static void Main(string[] args) { Thread t = new Thread(delegate() {Console.WriteLine("GO"); }); t.Start(); }
2、线程执行有参数的方法
有时候我们需要给Go方法传参来完成整个任务,但我们不能使用ThreadStart委托,因为它不接受参数,所幸的是,.NET framework定义了另一个版本的委托叫做ParameterizedThreadStart, 它可以接收一个单独的object类型参数:
class Program { static void Main(string[] args) { Thread t = new Thread(Go); t.Start(true); } public static void Go(object j) { Console.WriteLine("GO"); } }
ParameterizedThreadStart的特性是在使用之前我们必需对我们想要的类型(这里是bool)进行装箱操作,并且它只能接收一个参数。 一个替代方案是使用一个匿名方法调用一个普通的方法如下:
static void Main(string[] args) { Thread t = new Thread(delegate() { Go("jack", "hello"); }); t.Start(); Console.ReadKey(); } public static void Go(string name,string saying) { Console.WriteLine("{0},{1}",name,saying); }
优点是目标方法(这里是Go),可以接收任意数量的参数,并且没有装箱操作。不过这需要将一个外部变量放入到匿名方法中,匿名方法打开了一种怪异的现象,当外部变量被后来的部分修改了值的时候,可能会透过外部变量进行无意的互动。有意的互动(通常通过字段)被认为是足够了!一旦线程开始运行了,外部变量最好被处理成只读的——除非有人愿意使用适当的锁。
static void Main(string[] args) { string test = "Tom"; Thread t = new Thread(delegate() { Go(test, "hello"); }); test = "Jack"; t.Start(); Console.ReadKey(); } public static void Go(string name,string saying) { Console.WriteLine("{0},{1}",name,saying); }
另一种较常见的方式是将对象实例的方法而不是静态方法传入到线程中,对象实例的属性可以告诉线程要做什么
class Program { bool lower = false; static void Main(string[] args) { Program test = new Program(); test.lower = true; Thread t = new Thread(test.Go); t.Start(); Console.ReadKey(); } void Go() { Console.WriteLine(lower?"Hello":"hello"); } }
二、Thread的属性
1、静态属性和动态属性
静态属性包括CurrentThread ,CurrentContext,CurrentPrincipal(负责人);动态属性包括Priority,ThreadState ,IsAlive,IsBackground,IsThreadPoolThread,ManagedThreadId,ApartmentState,CurrentCulture,CurrentUICulture,ExecutionContext,Name
- CurrentThread 和Name的使用
线程可以通过它的Name属性进行命名,这非常有利于调试:可以用Console.WriteLine打印出线程的名字,Microsoft Visual Studio可以将线程的名字显示在调试工具栏的位置上。线程的名字可以在被任何时间设置——但只能设置一次,重命名会引发异常。 CurrentThread 获取正在运行的线程。
static void Main(string[] args) { Thread t = new Thread(Go); t.Name = "我是子线程"; t.Start(); Console.ReadKey(); } static void Go() { string currentThread = Thread.CurrentThread.Name; Console.WriteLine(currentThread); }
- CurrentContext:获取正在执行线程的当前上下文(线程上下文是指:线程运行需要的环境,参数等条件)
static void Main(string[] args) { Thread t1 = new Thread(Go); t1.Start(); Console.ReadKey(); } static void Go() { //获取当前上下文的上下文ID int currentContextId = Thread.CurrentContext.ContextID; Console.WriteLine(currentContextId); }
- CurrentPrincipal 获取或设置线程的当前负责人(对基于角色的安全性而言)
static void Main(string[] args) { string[] rolesArray = {"managers", "executives"}; try { // 设置当前负责人。从用户表示和角色名称数组初始化 Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("Bob", "Passport"), rolesArray); } catch(SecurityException secureException) { Console.WriteLine("{0}: Permission to set Principal " + "is denied.", secureException.GetType().Name); } IPrincipal threadPrincipal = Thread.CurrentPrincipal; Console.WriteLine("Name: {0} 是否认证: {1}" +" 认证的类别: {2}", threadPrincipal.Identity.Name, threadPrincipal.Identity.IsAuthenticated, threadPrincipal.Identity.AuthenticationType); Console.ReadKey() ; }
- IsAlive 获取一个值,该值指示当前线程的执行状态。经测试只有 Unstarted、Stopped 返回false;其他线程状态都返回true。它的定义如下:只读。
public bool IsAlive { get; }
- IsBackground 获取或设置一个值,该值指示某个线程是否为后台线程。它的定义如下:可以读写操作。
public bool IsBackground { get; [HostProtectionAttribute(SecurityAction.LinkDemand, SelfAffectingThreading = true)] set; }
线程默认为前台线程,这意味着任何前台线程在运行都会保持程序存活。C#也支持后台线程,当所有前台线程结束后,它们不维持程序的存活。 改变线程从前台到后台不会以任何方式改变它在CPU协调程序中的优先级和状态。线程的IsBackground属性控制它的前后台状态,如下实例:
class PriorityTest { static void Main (string[] args) { Thread worker = new Thread (delegate() { Console.ReadLine(); }); if (args.Length > 0) worker.IsBackground = true; worker.Start(); } }
- Priority 获取或设置一个值,该值指示线程的调度优先级。
优先级别有以下五种:Highest、AboveNormal、Normal、BelowNormal、Lowest。默认为Normal.只有多个线程同时为活动时,优先级才有作用。 设置一个线程的优先级为高一些,并不意味着它能执行实时的工作,因为它受限于程序的进程的级别。要执行实时的工作,必须提升在System.Diagnostics 命名空间下Process的级别,像下面这样:
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
ProcessPriorityClass.High 其实是一个短暂缺口的过程中的最高优先级别:Realtime。设置进程级别到Realtime通知操作系统:你不想让你的进程被抢占了。如果你的程序进入一个偶然的死循环,可以预期,操作系统被锁住了,除了关机没有什么可以拯救你了!基于此,High大体上被认为最高的有用进程级别。
- IsThreadPoolThread 获取一个值,该值指示线程是否属于托管线程池
using System; using System.Threading; class IsThreadPool { static void Main() { AutoResetEvent autoEvent = new AutoResetEvent(false); Thread regularThread = new Thread(new ThreadStart(ThreadMethod)); regularThread.Start(); ThreadPool.QueueUserWorkItem(new WaitCallback(WorkMethod),autoEvent); // Wait for foreground thread to end. regularThread.Join(); // Wait for background thread to end. autoEvent.WaitOne(); Console.ReadKey(); } static void ThreadMethod() { Console.WriteLine("ThreadOne, executing ThreadMethod, " + "is {0}from the thread pool.",Thread.CurrentThread.IsThreadPoolThread ? "" : "not "); } static void WorkMethod(object stateInfo) { Console.WriteLine("ThreadTwo, executing WorkMethod, " + "is {0}from the thread pool.",Thread.CurrentThread.IsThreadPoolThread ? "" : "not "); // Signal that this thread is finished. ((AutoResetEvent)stateInfo).Set(); } }
- ManagedThreadId 获取当前托管线程的惟一标识符:它的定义如下:只读
public int ManagedThreadId { get; }
- ApartmentState 获取或设置此线程的单元状态
using System; using System.Threading; class ApartmentTest { static void Main() { Thread newThread =new Thread(new ThreadStart(ThreadMethod)); newThread.SetApartmentState(ApartmentState.MTA); Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",newThread.ThreadState, newThread.ApartmentState); newThread.Start(); // Wait for newThread to start and go to sleep. Thread.Sleep(300); try { // This causes an exception since newThread is sleeping. newThread.SetApartmentState(ApartmentState.STA); } catch (ThreadStateException stateException) { Console.WriteLine(" {0} caught: " +"Thread is not in the Unstarted or Running state.",stateException.GetType().Name); Console.WriteLine("ThreadState: {0}, ApartmentState: {1}",newThread.ThreadState, newThread.GetApartmentState()); } Console.ReadKey(); } static void ThreadMethod() { Thread.Sleep(1000); } }
- CurrentCulture 获取或设置当前线程的区域性
using System; using System.Threading; using System.Windows.Forms; class UICulture : Form { public UICulture() { // Set the user interface to display in the // same culture as that set in Control Panel. Thread.CurrentThread.CurrentUICulture =Thread.CurrentThread.CurrentCulture; // Add additional code. } static void Main() { Application.Run(new UICulture()); } }
- CurrentUICulture 获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源
using System; using System.Globalization; using System.Threading; public class Example { public static void Main() { // Change the current culture if the language is not French. CultureInfo current = Thread.CurrentThread.CurrentUICulture; if (current.TwoLetterISOLanguageName != "fr") { CultureInfo newCulture = CultureInfo.CreateSpecificCulture("en-US"); Thread.CurrentThread.CurrentUICulture = newCulture; // Make current UI culture consistent with current culture. Thread.CurrentThread.CurrentCulture = newCulture; } Console.WriteLine("The current UI culture is {0} [{1}]", Thread.CurrentThread.CurrentUICulture.NativeName, Thread.CurrentThread.CurrentUICulture.Name); Console.WriteLine("The current culture is {0} [{1}]", Thread.CurrentThread.CurrentUICulture.NativeName, Thread.CurrentThread.CurrentUICulture.Name); Console.ReadKey(); } }
三、Thread 全部方法
Abort() |
在调用此方法的线程上引发ThreadAbortException,以开始终止此线程的过程。 调用此方法通常会终止线程 |
AllocateDataSlot |
在所有的线程上分配未命名的数据槽 |
AllocateNamedDataSlot |
在所有线程上分配已命名的数据槽 |
BeginCriticalRegion |
通知宿主执行将要进入一个代码区域,在该代码区域内线程中止或未处理的异常 的影响可能会危害应用程序域中的其他任务 |
BeginThreadAffinity | 通知宿主托管代码将要执行依赖于当前物理操作系统线程的标识的指令 |
EndThreadAffinity |
通知宿主托管代码已执行完依赖于当前物理操作系统线程的标识的指令 |
Equals |
确定两个Object实例是否相等 |
FreeNamedDataSlot |
为进程中的所有线程消除名称与槽之间的关联
|
GetApartmentState |
返回一个ApartmentState值,该值指示单元状态 |
GetCompressedStack |
返回一个 CompressedStack 对象,该对象可用于捕获当前线程的堆栈 |
GetData | 在当前线程的当前域中从当前线程上指定的槽中检索值 |
GetDomain |
返回当前线程正在其中运行的当前域 |
GetDomainID | 返回惟一的应用程序域标识符 |
GetHashCode |
返回当前线程的哈希代码 |
GetNamedDataSlot |
查找已命名的数据槽 |
Interrupt |
中断处于WaitSleepjoin线程状态的线程
|
Join | 阻止调用线程,直到某个线程终止时为止 |
MemoryBarrier |
同步内存。其效果是将缓存内存中的内容刷新到主内存中,从而使处理器能执行 当前线程 |
ReferenceEquals |
确定指定的Object实例是否是相同的实例 |
ResetAbort |
取消为当前线程请求的Abort |
Resume |
继续已挂起的线程
|
SetApartmentState |
在线程启动前设置其单元状态
|
SetCompressedStack |
对当前线程应用捕获的 CompressedStack |
SetData |
在当前正在运行的线程上为此线程的当前域在指定槽中设置数据 |
Sleep | 将当前线程阻止指定的毫秒数 |
SpinWait |
导致线程等待由 iterations 参数定义的时间量
|
Start |
使线程被安排进行执行 |
Suspend |
挂起线程,或者如果线程已挂起,则不起作用 |
ToString |
返回表示当前Object的String |
TrySetApartmentState | 在线程启动前设置其单元状态 |
VolatileRead |
读取字段值。无论处理器的数目或处理器缓存的状态如何,该值都是由计算机的 任何处理器写入的最新值 |
VolatileWrite |
立即向字段写入一个值,以使该值对计算机中的所有处理器都可见 |
1、Thread常用的方法解析
常用方法:Start(),Abort(),Suspend(),Resume(), Join(),Interrupt()以及静态方法Sleep()和ResetAbort()
- Start()开始线程。调用Start()开始一个线程。
一旦线程由于调用 Start 而离开 Unstarted 状态,那么它将无法再返回到 Unstarted 状态(最后被销毁)。
- Abort()和Thread.ResetAbort() 线程销毁及取消销毁。
调用线程的Abort()实例方法可以销毁目标线程实例,调用Thread.ResetAbort() 来取消线程销毁。
- Sleep()阻塞线程
调用Sleep()方法使当前线程放弃剩余时间片,立即挂起(阻塞)并且在指定时间内不被调度。应用实例:轮询休眠while (!proceed) Thread.Sleep (x); // "轮询休眠!"
- Suspend()与Resume()线程的挂起和唤醒
可结合Suspend()与Resume()来挂起和唤醒线程,这两方法已过时。当对某线程调用Suspend()时,系统会让该线程执行到一个安全点,然后才实际挂起该线程(与Thread.Sleep()不同, Suspend()不会导致线程立即停止执行)。无论调用了多少次 Suspend(),调用Resume()均会使另一个线程脱离挂起状态,并导致该线程继续执行。
- Join()阻塞线程
在线程A中调用线程B的Join()实例方法。在继续执行标准的 COM 和 SendMessage 消息泵处理期间,线程A将被阻塞,直到线程B终止为止。