相信只要写过网络的朋友,应该对这样的结构在熟悉不过了。accept后线程被挂起,等待一个客户发出请求,而后创建新线程来处理请求。当新线程处理客户请求时,起初的线程循环回去等待另一个客户请求。处理客户请求的线程处理完毕后终结。
在上述的并发模型中,对每个客户请求都创建了一个线程。其优点在于等待请求的线程只需做很少的工作。大多数时间中,该线程在休眠[因为recv处于堵塞状态]。
但是当并发模型应用在服务器端[基于Windows NT],Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的[没有被挂起和等待发生什么事],Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context],线程就没有得到很多CPU时间来做它们的工作。
大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没有开销的。
我们不妨设想一下:如果事先开好N个线程,让它们在那hold[堵塞],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出来的问题,Microsoft又怎会没有考虑到呢?!
这个问题的解决方法就是一个称为I/O完成端口的内核对象,他首次在Windows NT3.5中被引入。
其实我们上面的构想应该就差不多是IOCP的设计机理。其实说穿了IOCP不就是一个消息队列嘛!你说这和[端口]这两字有何联系。我的理解就是IOCP最多是应用程序和操作系统沟通的一个接口罢了。
以上是引用在网上看到的文字,在这里介绍下完成端口的设计理念。仔细想想完成端口,和线程池原理相通。下面完成一个自定义线程池的例子。
有两部分,一个线程管理类ThreadManager,一个线程类MyThread.
参考资料:
下面是代码,希望大家提出更好的建议:
1.ThreadManager.cs 2.MyThread.cs
using
System;
namespace Sweecom.Fee.ThreadM
{
/// <summary>
/// 类名称:CustomThreadPool
/// 类功能:线程管理器,会开启或唤醒一个线程去执行指定的回调方法
/// </summary>
public class ThreadManage
{
const Int32 _MaxCount = 10 ;
/// <summary>
/// 最大工作线程数
/// </summary>
private static Int32 MaxCount = _MaxCount;
/// <summary>
/// 静态,线程列表
/// </summary>
public static System.Collections.ArrayList threadList = new System.Collections.ArrayList();
/// <summary>
/// 静态方法,开启或唤醒一个线程去执行指定的回调方法
/// </summary>
/// <param name="CompID"> 企业ID </param>
/// <param name="UserID"> 用户编号 </param>
/// <param name="timeOut"> 当没有可用的线程时的等待时间,以毫秒为单位 </param>
/// <param name="Type"> 1为接收,2为发送 </param>
/// <returns></returns>
public static bool QueueUserWorkItem(Int32 CompID,String UserID,Int32 tiemOut,Int32 Type)
{
Boolean flag = false ;
// 锁住共享资源,实现线程安全
lock (threadList)
{
try
{
// 如果线程列表为空,填充线程列表
if (threadList.Count == 0 )
{
InitThreadList();
}
Int64 startTime = DateTime.Now.Ticks;
do
{
// 遍历线程列表,找出可用线程
foreach (MyThread myThread in threadList)
{
// 线程为空,需要创建线程
if (myThread.thread == null )
{
myThread.Start(CompID,UserID, false ,Type);
flag = true ;
break ;
}
else if (myThread.thread.ThreadState == System.Threading.ThreadState.Suspended)
{
// 线程为挂起状态,唤醒线程
myThread.Start(CompID,UserID, true ,Type);
flag = true ;
break ;
}
}
// 当没有可用线程时,sleep
// 在线程sleep前释放锁
System.Threading.Monitor.PulseAll(threadList);
System.Threading.Thread.Sleep( 500 );
} while (((DateTime.Now.Ticks - startTime) / 1000 ) < tiemOut && ! flag);
}
catch (Exception ex)
{
throw ex;
}
finally
{
System.Threading.Monitor.Exit(threadList);
}
}
return flag;
}
/// <summary>
/// 使用MyThread对象填充线程列表,注意,这个时候线程并没有启动
/// </summary>
public static void InitThreadList()
{
threadList = new System.Collections.ArrayList();
for ( int i = 0 ;i < MaxCount;i ++ )
{
MyThread myThread = new MyThread();
threadList.Add(myThread);
}
}
/// <summary>
/// 不允许创建实例
/// </summary>
private ThreadManage()
{
}
}
}
namespace Sweecom.Fee.ThreadM
{
/// <summary>
/// 类名称:CustomThreadPool
/// 类功能:线程管理器,会开启或唤醒一个线程去执行指定的回调方法
/// </summary>
public class ThreadManage
{
const Int32 _MaxCount = 10 ;
/// <summary>
/// 最大工作线程数
/// </summary>
private static Int32 MaxCount = _MaxCount;
/// <summary>
/// 静态,线程列表
/// </summary>
public static System.Collections.ArrayList threadList = new System.Collections.ArrayList();
/// <summary>
/// 静态方法,开启或唤醒一个线程去执行指定的回调方法
/// </summary>
/// <param name="CompID"> 企业ID </param>
/// <param name="UserID"> 用户编号 </param>
/// <param name="timeOut"> 当没有可用的线程时的等待时间,以毫秒为单位 </param>
/// <param name="Type"> 1为接收,2为发送 </param>
/// <returns></returns>
public static bool QueueUserWorkItem(Int32 CompID,String UserID,Int32 tiemOut,Int32 Type)
{
Boolean flag = false ;
// 锁住共享资源,实现线程安全
lock (threadList)
{
try
{
// 如果线程列表为空,填充线程列表
if (threadList.Count == 0 )
{
InitThreadList();
}
Int64 startTime = DateTime.Now.Ticks;
do
{
// 遍历线程列表,找出可用线程
foreach (MyThread myThread in threadList)
{
// 线程为空,需要创建线程
if (myThread.thread == null )
{
myThread.Start(CompID,UserID, false ,Type);
flag = true ;
break ;
}
else if (myThread.thread.ThreadState == System.Threading.ThreadState.Suspended)
{
// 线程为挂起状态,唤醒线程
myThread.Start(CompID,UserID, true ,Type);
flag = true ;
break ;
}
}
// 当没有可用线程时,sleep
// 在线程sleep前释放锁
System.Threading.Monitor.PulseAll(threadList);
System.Threading.Thread.Sleep( 500 );
} while (((DateTime.Now.Ticks - startTime) / 1000 ) < tiemOut && ! flag);
}
catch (Exception ex)
{
throw ex;
}
finally
{
System.Threading.Monitor.Exit(threadList);
}
}
return flag;
}
/// <summary>
/// 使用MyThread对象填充线程列表,注意,这个时候线程并没有启动
/// </summary>
public static void InitThreadList()
{
threadList = new System.Collections.ArrayList();
for ( int i = 0 ;i < MaxCount;i ++ )
{
MyThread myThread = new MyThread();
threadList.Add(myThread);
}
}
/// <summary>
/// 不允许创建实例
/// </summary>
private ThreadManage()
{
}
}
}
下面就介绍一个简单的问题,给线程传参的问题。大家会发现,线程是无法传参数的,那我们就只有赋值给其他变量,由程序自己去访问这些变量进行串参数,或使用(对象。方法)在构造函数中赋值。
using
System;
namespace Sweecom.Fee.ThreadM
{
/// <summary>
/// 类名称:MyThread
/// 类功能:封装 .NET 框架提供的 Thread
/// </summary>
public class MyThread
{
/// <summary>
/// 线程
/// </summary>
private System.Threading.Thread _thread;
/// <summary>
/// 执行回调方法的线程
/// </summary>
public System.Threading.Thread thread
{
get
{
return _thread;
}
}
/// <summary>
/// 企业ID
/// </summary>
private Int32 CompID;
/// <summary>
/// 用户编号
/// </summary>
private String UserID;
/// <summary>
/// 1为接收,2为发送
/// </summary>
private Int32 Type;
/// <summary>
/// 开启新线程或唤醒线程,去执行回调方法
/// </summary>
/// <param name="CompID"> 企业ID </param>
/// <param name="UserID"> 用户编号 </param>
/// <param name="isSuspend"> true 表示线程为挂起状态,false 则表示线程还没创建 </param>
public void Start(Int32 _CompID,String _UserID,Boolean isSuspend,Int32 _Type)
{
// 开启新线程或唤醒线程前,先设置
Type = _Type;
CompID = _CompID;
UserID = _UserID;
// 线程为挂起状态,唤醒线程继续执行
if (isSuspend)
_thread.Resume();
else
{ // 线程还没有创建,创建一个新线程,并执行
_thread = new System.Threading.Thread( new System.Threading.ThreadStart(ThreadProc));
_thread.Start();
}
}
/// <summary>
/// 线程执行的方法
/// </summary>
private void ThreadProc()
{
try
{
// 死循环,使线程唤醒后不是退出,而是继续通过委托执行回调方法
while ( true )
{
// 要执行的方法
if (CompID != 0 && UserID.ToString() != "" && Type == 1 )
Incept.FeeExecute.MakeFee(UserID,CompID);
if (CompID != 0 && UserID.ToString() != "" && Type == 2 )
Send.FeeExecute.MakeFee(UserID,CompID);
if (_thread.ThreadState != System.Threading.ThreadState.Suspended)
_thread.Suspend();
System.Threading.Thread.Sleep( 1 );
}
}
catch (Exception ex)
{
throw ex;
}
}
public MyThread()
{
}
}
}
namespace Sweecom.Fee.ThreadM
{
/// <summary>
/// 类名称:MyThread
/// 类功能:封装 .NET 框架提供的 Thread
/// </summary>
public class MyThread
{
/// <summary>
/// 线程
/// </summary>
private System.Threading.Thread _thread;
/// <summary>
/// 执行回调方法的线程
/// </summary>
public System.Threading.Thread thread
{
get
{
return _thread;
}
}
/// <summary>
/// 企业ID
/// </summary>
private Int32 CompID;
/// <summary>
/// 用户编号
/// </summary>
private String UserID;
/// <summary>
/// 1为接收,2为发送
/// </summary>
private Int32 Type;
/// <summary>
/// 开启新线程或唤醒线程,去执行回调方法
/// </summary>
/// <param name="CompID"> 企业ID </param>
/// <param name="UserID"> 用户编号 </param>
/// <param name="isSuspend"> true 表示线程为挂起状态,false 则表示线程还没创建 </param>
public void Start(Int32 _CompID,String _UserID,Boolean isSuspend,Int32 _Type)
{
// 开启新线程或唤醒线程前,先设置
Type = _Type;
CompID = _CompID;
UserID = _UserID;
// 线程为挂起状态,唤醒线程继续执行
if (isSuspend)
_thread.Resume();
else
{ // 线程还没有创建,创建一个新线程,并执行
_thread = new System.Threading.Thread( new System.Threading.ThreadStart(ThreadProc));
_thread.Start();
}
}
/// <summary>
/// 线程执行的方法
/// </summary>
private void ThreadProc()
{
try
{
// 死循环,使线程唤醒后不是退出,而是继续通过委托执行回调方法
while ( true )
{
// 要执行的方法
if (CompID != 0 && UserID.ToString() != "" && Type == 1 )
Incept.FeeExecute.MakeFee(UserID,CompID);
if (CompID != 0 && UserID.ToString() != "" && Type == 2 )
Send.FeeExecute.MakeFee(UserID,CompID);
if (_thread.ThreadState != System.Threading.ThreadState.Suspended)
_thread.Suspend();
System.Threading.Thread.Sleep( 1 );
}
}
catch (Exception ex)
{
throw ex;
}
}
public MyThread()
{
}
}
}