线程池是后台线程. 每个线程都使用默认的堆栈大小, 以默认的优先级运行, 并处于多线程单元中. 每个进程只有一个线程池对象.
下面说一下线程池中的异常, 在线程池中未处理的异常将终止进程. 以下为此规则的三种例外情况:
(1)由于调用了 Abort, 线程池线程中将引发 ThreadAbortException 异常(在对 Abort 方法进行调用时引发的异常).
(2)由于正在卸载应用程序域, 线程池线程中将引发 AppDomainUnloadedException 异常(在尝试访问已卸载的应用程序域时引发的异常).
(3)公共语言运行库或宿主进程将终止线程.
如果公共语言运行库所创建的线程中未处理这些异常中的任何一个, 则异常将终止线程, 但公共语言运行库不允许该异常继续下去.
如果在主线程或从非托管代码进入运行库的线程中未处理这些异常, 则它们将正常继续, 并导致应用程序终止.
注意: 在 .NET Framework 1.0 和 1.1 版中, 公共语言运行库将捕获线程池中的未处理异常, 而不出现任何提示. 这可能会破坏应用程序状态, 并最终导致应用程序挂起, 将很难进行调试.
使用线程池的方式主要有 4 种, 下面分别对其进行介绍.
1.ThreadPool 类的 QueueUserWorkItem 方法
在使用线程池时, 可以从托管代码中调用 ThreadPool 类的 QueueUserWorkItem 方法, 或从非托管代码中调用 CorQueueUserWorkItem 方法, 并用线程池线程要执行的回调方法 WaitCallback 执行线程池.
QueueUserWorkItem 方法将方法排入队列以便执行(并指定包含该方法所用数据的对象, 用 state 参数来实现). 此方法在有线程池线程变得可用时执行. 该方法有两个语法形式, 其语法如下:
- public static bool QueueUserWorkItem(WaitCallback callBack)
- public static bool QueueUserWorkItem(WaitCallback callBack,Object state)
参数说明:
callBack:WaitCallback 类型, 它表示要执行的方法.
State:Object 类型, 包含方法所用数据的对象.
返回值: Boolean 类型, 如果此方法成功排队, 则为 true; 如果无法将该工作项排队, 则引发 OutOfMemoryException 异常.
示例 线程池的应用
本示例用线程池顺序执行两个方法. 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- namespace ThreadPoolApple
- {
- class Program
- {
- public void thread1(Object obj)// 定义方法 thread1
- {
- for (int i = 0; i <= 3; i++)// 输出 0~3 的值
- {
- Console.Write(i.ToString());
- }
- Console.WriteLine();// 换行
- }
- public void thread2(Object obj)// 定义方法 thread2
- {
- for (int i = 4; i <= 6; i++)// 输出 4~6 的值
- {
- Console.Write(i.ToString() + obj.ToString());// 值后面加货币符号
- }
- Console.WriteLine();// 换行
- }
- static void Main(string[] args)
- {
- string Ostr = "¥";// 用字符串记录货币符号
- Program prog = new Program();// 实例化 Program 类
- for (int i = 0; i <= 3; i++)
- {
- // 用线程池执行无参数方法
- ThreadPool.QueueUserWorkItem(new WaitCallback(prog.thread1));
- // 用线程池执行有参数方法
- ThreadPool.QueueUserWorkItem(new WaitCallback(prog.thread2),Ostr);
- }
- Console.ReadLine();
- }
- }
- }
运行结果如下图所示.
图 线程池的应用结果
注意: 在 ThreadPool 类中 QueueUserWorkItem 是一个静态方法, 因此可以由 ThreadPool 类直接用.
2.ThreadPool 类的 UnsafeQueueUserWorkItem 方法
该方法注册一个等待 WaitHandle 的委托. 与 QueueUserWorkItem 方法不同, UnsafeQueueUserWorkItem 不会将调用堆栈传播到辅助线程. 这使得代码可以失去调用堆栈, 从而提升它的安全特权. 语法如下:
- [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.NoFlags|SecurityPermissionFlag.ControlEvidence|SecurityPermissionFlag.ControlPolicy)]
- public static bool UnsafeQueueUserWorkItem(WaitCallback callBack,Object state)
参数说明:
callBack :System.Threading.WaitCallback 类型, 一个 WaitCallback, 表示当线程池中的线程选择工作项时调用的委托.
state:Object 类型, 在接受线程池服务时传递给委托的对象.
返回值: Boolean 类型, 如果方法成功, 则为 true; 如果无法将该工作项排队, 则引发 OutOfMemoryException.
在使用 UnsafeQueueUserWorkItem 方法时, 可能会无意中打开一个安全漏洞. 代码访问安全性的权限检查基于所有调用方对堆栈的权限进行. 如果使用 UnsafeQueueUserWorkItem 将工作排在某个线程池线程上, 则该线程池线程的堆栈将不会具有实际调用方的上下文. 恶意代码可能会利用这一点避开权限检查.
示例 线程池的应用
本示例主要讲解一下如何用 UnsafeQueueUserWorkItem 方法对线程池进行操作. 代码如下:
- namespace _01_05
- {
- class Program
- {
- static void Main(string[] args)
- {
- // 注册一个等待 WaitHandle 的委托
- ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(ThreadProc), "First task");
- Console.WriteLine("将主线程挂起");
- Thread.Sleep(1000);// 挂走主线程
- Console.WriteLine("执行主线程");
- Console.ReadLine();
- }
- // 自定义方法, 主要用于线程池的调用方法
- public static void ThreadProc(object state)
- {
- Console.WriteLine("执行线程池"+state.ToString());
- }
- }
- }
运行结果如下图所示.
与 QueueUserWorkItem 方法不同, UnsafeQueueUserWorkItem 不会将调用堆栈传播到辅助线程. 这使得代码可以失去调用堆栈, 从而提升它的安全特权.
3.ThreadPool 类的 RegisterWaitForSingleObject 方法
可以使用 ThreadPool 类的 RegisterWaitForSingleObject 方法注册正在等待 WaitHandle 的委托. 该方法一共有 4 个语法形式, 下面分别对其进行介绍.
l 语法 1
注册一个等待 WaitHandle 的委托, 并指定一个 32 位有符号整数来表示超时值(以毫秒为单位). 其语法如下:
public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,int millisecondsTimeOutInterval,bool executeOnlyOnce)
waitObject:WaitHandle 类型, 要注册的 WaitHandle. 使用 WaitHandle 而非 Mutex.
CallBack:WaitOrTimerCallback 类型, waitObject 参数终止时调用的 WaitOrTimerCallback 委托.
State:Object 类型, 传递给委托的对象.
MillisecondsTimeOutInterval:Int32 类型, 以毫秒为单位的超时. 如果 millisecondsTimeOutInterval 参数为 0(零), 函数将测试对象的状态并立即返回. 如果 millisecondsTimeOutInterval 为 - 1, 则函数的超时间隔永远不过期.
ExecuteOnlyOnce:Boolean 类型, 如果为 true, 表示在调用了委托后, 线程将不再在 waitObject 参数上等待; 如果为 false, 表示每次完成等待操作后都重置计时器, 直到注销等待.
返回值: System.Threading.RegisteredWaitHandle 类型, 封装本机句柄的 RegisteredWaitHandle.
l 语法 2
注册一个等待 WaitHandle 的委托, 并指定一个 64 位有符号整数来表示超时值(以毫秒为单位). 其语法如下:
public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,long millisecondsTimeOutInterval,bool executeOnlyOnce)
参数说明:
waitObject:WaitHandle 类型, 要注册的 WaitHandle. 使用 WaitHandle 而非 Mutex.
CallBack:WaitOrTimerCallback 类型, waitObject 参数终止时调用的 WaitOrTimerCallback 委托.
State:Object 类型, 传递给委托的对象.
millisecondsTimeOutInterval:Int64 类型, 以毫秒为单位的超时. 如果 millisecondsTimeOutInterval 参数为 0(零), 函数将测试对象的状态并立即返回. 如果 millisecondsTimeOutInterval 为 - 1, 则函数的超时间隔永远不过期.
ExecuteOnlyOnce:Boolean 类型, 如果为 true, 表示在调用了委托后, 线程将不再在 waitObject 参数上等待; 如果为 false, 表示每次完成等待操作后都重置计时器, 直到注销等待.
返回值: System.Threading.RegisteredWaitHandle 类型, 封装本机句柄的 RegisteredWaitHandle.
l 语法 3
注册一个等待 WaitHandle 的委托, 并指定一个 TimeSpan 值来表示超时时间. 其语法如下:
public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,TimeSpan timeout,bool xecuteOnlyOnce)
参数说明:
waitObject:WaitHandle 类型, 要注册的 WaitHandle. 使用 WaitHandle 而非 Mutex.
CallBack:WaitOrTimerCallback 类型, waitObject 参数终止时调用的 WaitOrTimerCallback 委托.
State:Object 类型, 传递给委托的对象.
Timeout:TimeSpan 类型, TimeSpan 表示的超时时间. 如果 timeout 为 0(零), 则函数将测试对象的状态并立即返回. 如果 timeout 为 - 1, 则函数的超时间隔永远不过期.
ExecuteOnlyOnce:Boolean 类型, 如果为 true, 表示在调用了委托后, 线程将不再在 waitObject 参数上等待; 如果为 false, 表示每次完成等待操作后都重置计时器, 直到注销等待.
返回值: System.Threading.RegisteredWaitHandle 类型, 封装本机句柄的 RegisteredWaitHandle.
l 语法 4
指定表示超时 (以毫秒为单位) 的 32 位无符号整数, 注册一个委托等待 WaitHandle. 其语法如下:
- [CLSCompliantAttribute(false)]
- public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle waitObject,WaitOrTimerCallback callBack,Object state,uint millisecondsTimeOutInterval,bool executeOnlyOnce)
参数说明:
waitObject:WaitHandle 类型, 要注册的 WaitHandle. 使用 WaitHandle 而非 Mutex.
CallBack:WaitOrTimerCallback 类型, waitObject 参数终止时调用的 WaitOrTimerCallback 委托.
State:Object 类型, 传递给委托的对象.
MillisecondsTimeOutInterval:UInt32 类型, 以毫秒为单位的超时. 如果 millisecondsTimeOutInterval 参数为 0(零), 函数将测试对象的状态并立即返回. 如果 millisecondsTimeOutInterval 为 - 1, 则函数的超时间隔永远不过期.
ExecuteOnlyOnce:Boolean 类型, 如果为 true, 表示在调用了委托后, 线程将不再在 waitObject 参数上等待; 如果为 false, 表示每次完成等待操作后都重置计时器, 直到注销等待.
返回值: System.Threading.RegisteredWaitHandle 类型, 封装本机句柄的 RegisteredWaitHandle.
注意: 在以上的 4 个语法中, 对 waitObject 应用 Mutex 不会导致回调互斥, 因为它是基于 Win32 API 使用默认的 WT_EXECUTEDEFAULT 标志, 所以每次回调都在单独的线程池线程上调度. 因此, 请尽可能的不要使用 Mutex, 应该使用最大计数为 1 的 Semaphore.
RegisterWaitForSingleObject 方法将指定的委托排队到线程池. 当发生以下两种情况时, 辅助线程将执行委托:
l 指定对象处于终止状态.
l 超时间隔已过期.
RegisterWaitForSingleObject 方法检查指定对象的 WaitHandle 的当前状态. 如果对象状态为非终止状态, 则此方法将注册一个等待操作, 该等待操作由线程池中的一个线程来执行. 当对象状态变为终止或超时间隔已过期时, 委托由辅助线程执行. 如果 timeOutInterval 参数不为 0(零), 并且 executeOnlyOnce 参数为 false, 则每当事件收到信号或超时间隔过期时都会重置计时器. 若要取消等待操作, 请调用 RegisteredWaitHandle.Unregister 方法.
等待线程使用 Win32 的 WaitForMultipleObjects 函数来监视已注册的等待操作. 因此, 如果必须在对 RegisterWaitForSingleObject 的多次调用中使用相同的本机操作系统句柄, 则必须使用 Win32 的 DuplicateHandle 函数重复该句柄. 这里要注意的是, 不应为传递到 RegisterWaitForSingleObject 的事件对象发出脉冲, 这是因为等待线程在重置前可能不会检测到该事件已终止.
返回前, 函数将修改某些类型的同步对象的状态. 修改仅发生在其终止状态满足等待条件的对象上. 例如, 信号量计数减少一.
下面通过一个简单的例子, 来说明一下如何使用 RegisterWaitForSingleObject 方法对线程池进行相应的操作. 代码如下:
示例 线程池的应用
本示例通过注册正在等待的 WaitHandle 委托, 对线程池进行操作. 代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- namespace _01_04
- {
- class Program
- {
- public class TaskInfo// 定义一个类
- {
- public RegisteredWaitHandle Handle = null;// 定义一个句柄变量
- public string OtherInfo = "default";// 定义一个字符串
- }
- static void Main(string[] args)
- {
- AutoResetEvent ev = new AutoResetEvent(false);// 实例化等待线程已发生的事件, 将初始状态设置为非终止
- TaskInfo ti = new TaskInfo();// 实例化类
- ti.OtherInfo = "First task";// 记录文本信息
- ti.Handle = ThreadPool.RegisterWaitForSingleObject(
- ev,// 要进行注册的 WaitHandle 类型
- new WaitOrTimerCallback(WaitProc),// 当 WaitHandle 超时或终止时要调用的方法
- ti,// 传递给委托对象的值
- 1000,// 设置超时间隔
- false// 表示每次完成等待操作后都重置计时器, 直到注销等待
- );// 注册一个等待的委托
- Thread.Sleep(3100);// 将主线程挂起
- Console.WriteLine("执行主线程");
- ev.Set();// 将 WaitHandle 设置为终止
- Console.WriteLine("将等待线程已发生的事件设置为终止");
- Console.ReadLine();
- }
- //state: 一个对象, 包含回调方法在每次执行时要使用的信息
- //timedOut: 如果 WaitHandle 超时, 则为 true; 如果其终止, 则为 false.
- public static void WaitProc(object state, bool timedOut)
- {
- TaskInfo ti = (TaskInfo)state;
- if (!timedOut)// 当 WaitHandle 为终止时
- {
- if (ti.Handle != null)// 如果委托对象的句柄不为空
- ti.Handle.Unregister(null);// 取消 RegisterWaitForSingleObject 方法所发出的已注册等待操作
- }
- string StrTime = timedOut ? "WaitHandle 超时" : "终止";
- Console.WriteLine("当前执行所使用的信息:" + state.ToString() + ";" + StrTime + "; 传递给委托的对象:" + ti.OtherInfo);
- }
- }
- }
运行结果如下图所示.
图 RegisterWaitForSingleObject 方法的应用
在上图可以看出, 在上例并没有使用 for 等循环语句执行线程池 (ThreadPool) 中的 RegisterWaitForSingleObject 方法, 但它却执行了 3 次, 这是为什么呢? 其主要原因是将 RegisterWaitForSingleObject 方法中的 ExecuteOnlyOnce 参数设置为 false(如果将其设置为 true, 调用了委托后, 线程将不在 waitObject 参数上等待, 也就是只执行一次), 它表示每次完成等待操作后都重置计时器, 直到取消由 RegisterWaitForSingleObject 方法发出的已经注册等待的操作, 主要是用 State 参数来实现的, 为了可以在该参数中记录所传递的委托对象, 事先必须要定义一个类, 在类中定义两个全局变量, 用于记录委托的信息, 以及本机的句柄. 其类的定义如下:
- public class TaskInfo// 定义一个类
- {
- public RegisteredWaitHandle Handle = null;// 定义一个句柄变量
- public string OtherInfo = "default";// 定义一个字符串
- }
然后通过自定义类的 Handle 变量记录本机句柄, 用 OtherInfo 变量记录委托信息, 如果想要取消 RegisterWaitForSingleObject 方法所发出的已注册的等待操作, 可以用 Handle 变量的 Unregister(null)方法取消注册.
为了读者能更好的定义 RegisterWaitForSingleObject 方法所执行的自定义事件, 下面对 RegisterWaitForSingleObject 方法中 CallBack 参数所调用的方法进行说明, CallBack 参数是 WaitHandle 类型的, 主要是当 WaitHandle 超时或终止时所调用的方法, 其语法结构为:
- [ComVisibleAttribute(true)]
- public delegate void WaitOrTimerCallback(Object state,bool timedOut)
参数说明:
state:Object 类型, 一个对象, 包含回调方法在每次执行时要使用的信息.
TimedOut:Boolean 类型, 如果 WaitHandle 超时, 则为 true; 如果其终止, 则为 false.
通过以上语法, 用户可自定义一个方法, 用于线程池的调用, 代码如下:
- public static void WaitProc(object state, bool timedOut)
- {
- }
来源: https://www.cnblogs.com/DonetRen/p/10172047.html