但是,我在C#中对此进行了测试:
using System; using System.IO; using System.Threading; class Program { static void Main() { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); var sc = new SynchronizationContext(); SynchronizationContext.SetSynchronizationContext(sc); { var path = Environment.ExpandEnvironmentVariables( @"%SystemRoot%\Notepad.exe"); var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1024 * 4, true); var bytes = new byte[1024]; fs.BeginRead(bytes, 0, bytes.Length, ar => { sc.Post(dummy => { var res = fs.EndRead(ar); // Are we in the same thread? Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }, null); }, null); } Thread.Sleep(100); } }
结果是:
1 5
因此,它似乎是,相反的答案,线程开始读取和结束读线程是 不 一样的。
因此,现在我的问题是,如何在.NET中实现 单线程 ,基于事件的无阻塞异步Web服务器?
整个过程SetSynchronizationContext是一条红鲱鱼,这只是一种编组机制,工作仍在IO线程池中进行。
SetSynchronizationContext
您需要的是一种从主线程对所有IO工作进行排队和收集异步过程调用的方法。许多更高级别的框架都包装了这种功能,其中最著名的就是libevent。
这里的各种选项都有很好的概括:epoll,poll和threadpool之间有什么区别?。
.NET已经拥有一个特殊的“ IO线程池”来为您进行扩展,当您调用BeginXYZ方法时,该线程池可以处理IO访问。该IO线程池在包装盒上每个处理器必须至少具有1个线程。请参阅:ThreadPool.SetMaxThreads。
BeginXYZ
如果单线程应用是一个关键需求(出于某种疯狂的原因),那么您当然可以在使用DllImport的过程中互操作所有这些东西(请参见此处的示例)
但是,这将是一个非常复杂和冒险的任务:
我们为什么不支持APC作为完成机制?对于用户代码,APC确实不是一个好的通用完成机制。管理APC引入的可重入性几乎是不可能的。例如,每当您阻塞锁时,任意的I / O完成都可能会占用您的线程。它可能尝试获取自己的锁,这可能会引入锁排序问题,从而导致死锁。防止这种情况需要精心设计,并具有确保您的警报等待期间永远不会运行别人的代码的能力,反之亦然。这极大地限制了APC的用途。
因此,回顾一下。如果您想要一个使用APC和完成端口来完成其所有工作的 单线程 托管进程,则必须手动对其进行编码。构建它是冒险和棘手的。
如果您只想进行 大规模 联网,则可以继续使用BeginXYZ并保持家庭状态,并放心,因为它使用APC,所以性能良好。您只需支付少量费用即可在线程和.NET特定实现之间进行编组。
来自:http : //msdn.microsoft.com/zh-cn/magazine/cc300760.aspx
扩展服务器的下一步是使用异步I / O。异步I / O减轻了创建和管理线程的需要。这样可以简化代码,并且是更有效的I / O模型。异步I / O利用回调来处理传入的数据和连接,这意味着不需要设置和扫描列表,也无需创建新的工作线程来处理未决的I / O。
服务器的目标是通过使服务器的线程避免不必要的阻塞来尽可能少地进行上下文切换,同时通过使用多个线程来最大程度地提高并行度。理想的情况是,有一个线程在为每个处理器主动服务于一个客户端请求,并且对于那些线程,如果在完成请求时还有其他请求在等待,则这些线程不会阻塞。但是,要使其正常工作,必须有一种方法可以使应用程序在一个客户端请求的处理在I / O上阻塞时(例如,在处理过程中从文件中读取时)激活另一个线程。