您的位置:首页 > 其它

快速入门系列--CLR--02多线程

2015-12-31 20:45 309 查看
最近,由于基础框架的整体升级,因此需要更新所有相关项目的DLL文件。这个过程存在不小的风险,因此也对发布后的生产服务器进行了密切的监控,结果还是出现了个别应用出现异常的情况,很快的占用了大量的服务器内存和CPU等资源。通过研究dump,初步发现是由于配置服务器出现单点故障,然后应用通过多线程调用相关SOA服务时出现异常,引发了ThreadAbortException异常,而且由于原有异常处理代码不够严谨,而且与异步发送报警邮件紧密结合在一起,造成线程数量的几何级增加,最终使得整个服务器不可用。这儿介绍的不算太清楚,而且相关原因虽然都有一定说服力,但证据不足,所幸最后通过重构,拿掉不需要的多线程操作,服务恢复正常。但不管如何,也确实要好好学习.NET CLR下的多线程相关知识。身边的一个资深架构师给我们的建议是,尽可能不要创建线程,如果确实需要一定要控制线程的数量,并且要可追溯。此外,如果是在IIS中托管的CLR,线程池的限制很多,而且是CLR中所有的appdomain共享,容易出现意料不到的错误,推荐使用.NET新的异步模型TPL。

在CLR一书中,将与线程有关的内容主要分成了5部分:线程相关基础知识;计算限制的异步操作;I/O限制的一步操作;基本线程同步变量;混合线程同步变量。本文虽然不会使用这个分类,但是这个分类对于相关概念在脑海建立一个有机的整体很有帮助。

public void Test()
{
CallContext.LogicalSetData("name", "xionger");
ThreadPool.QueueUserWorkItem(s => Console.WriteLine("name; {0}", CallContext.LogicalGetData("name")));
//阻止线程上下文的流动
ExecutionContext.SuppressFlow();
ThreadPool.QueueUserWorkItem(s => Console.WriteLine("name; {0}", CallContext.LogicalGetData("name")));
//恢复线程上下文的流动
ExecutionContext.RestoreFlow();
}


View Code

完成端口模型(一个很老的Win32概念,可以无视)

之前可以看到I/O线程的名称叫CompletionPortThreads完成端口线程,这其实是Windows下的一种异步IO模型,其实可以把完成端口看成系统维护的一个队列,操作系统把重叠IO操作完成的事件通知放到该队列里,由于是暴露 "操作完成"的事件通知,所以命名为"完成端口"(Completion Ports)。一个socket被创建后,可以在任何时刻和一个完成端口联系起来。

一般来说,一个应用程序可以创建多个工作线程来处理完成端口上的通知事件。工作线程的数量依赖于程序的具体需要。但是在理想的情况下,应该对应一个CPU创建一个线程。因为在完成端口理想模型中,每个线程都可以从系统获得一个"原子"性的时间片,轮番运行并检查完成端口,线程的切换是额外的开销。在实际开发的时候,还要考虑这些线程是否牵涉到其他堵塞操作的情况。如果某线程进行堵塞操作,系统则将其挂起,让别的线程获得运行时间。因此,如果有这样的情况,可以多创建几个线程来尽量利用时间。

总之,开发一个可扩展的Winsock服务器并非十分困难的。主要是开始一个监听socket,接收连接,并且进行重叠发送和接收的IO操作。最大的挑战就是管理系统资源,限制重叠Io的数量,避免内存危机。遵循这几个原则,就能帮助你开发高性能,可扩展的服务程序。socket的接收缓冲,因为接收事件仅仅在AcceptEx调用中发生。保证每个socket都有一个接收缓冲不会造成什么危害。一旦客户端/服务器在最初的一次请求(由AcceptEx完成)之后进行交互,发送更多的数据,那么取消接收缓冲更是一个很不好的做法。除非你能保证这些数据都是在每个连接的重叠IO接收里完成的 。

参考资料:

Jeffrey, Richter. CLR via C#[M]. 北京:清华大学出版社, 2010.

风尘浪子. 细说多线程[EB/OL]. http://www.cnblogs.com/leslies2/archive/2012/02/07/2310495.html.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: