我正在用C#编写一个服务器,它响应客户端请求创建一个(长的,甚至无限的)IEnumerable<Result>
,然后将这些结果流回客户端。
我可以设置它,以便,如果客户端读取缓慢(或者一次可能几乎没有读取),服务器将不需要停止等待缓冲区空间清除的线程它可以提取下一对Result
,序列化它们并将它们填充到网络上?
这是NetworkStream.BeginWrite
的工作原理吗?关于何时调用回调方法的文档(对我来说)不清楚。它是否基本上立即发生,只是在另一个线程上然后阻塞EndWrite
等待实际写入发生?当套接字API中的某种低级缓冲区下溢时会发生这种情况吗?当数据实际写入网络时是否会发生?它被确认后会发生吗?
我很困惑,因此整个问题可能会偏离基础。如果是这样,你能转过身来指出我正确的方向来解决我期望的相当普遍的问题吗?
答案 0 :(得分:2)
我将更详细地回答你问题的第三部分。
MSDN文档声明:
当你的应用程序调用BeginWrite时,系统使用一个单独的线程来执行指定的回调方法,并在EndWrite上阻塞,直到NetworkStream发送所请求的字节数或抛出异常。
据我所知,调用BeginSend后是否立即调用回调方法取决于底层实现和平台。例如,如果Windows上的IO完成端口可用,则不会。线程池中的线程在调用之前将被阻塞。
实际上,NetworkStream的BeginWrite方法只是在我的.Net实现上调用底层套接字的BeginSend方法。我使用底层WSASend Winsock函数和完成端口(如果可用)。这使得它比为每个发送/写入操作简单地创建自己的线程更有效,即使您使用线程池也是如此。
如果WSASend的结果是IOPending,则Socket.BeginSend方法然后调用OverlappedAsyncResult.CheckAsyncCallOverlappedResult方法,IOPending又调用本机RegisterWaitForSingleObject Win32函数。这将导致线程池中的一个线程阻塞,直到WSASend方法发出信号表明它已完成,然后调用回调方法。
由NetworkStream.EndSend调用的Socket.EndSend方法将等待发送操作完成。它必须这样做是因为如果IO完成端口不可用,那么将立即调用回调方法。
我必须再次强调,这些细节特定于我对.Net和我的平台的实现,但这应该会给你一些见解。
答案 1 :(得分:0)
首先,主线程在完成其他工作时可以继续执行的唯一方法是使用另一个线程。一个线程不能同时做两件事。
但是,我认为你想要避免的是Thread对象的混乱,是的,这可以通过使用BeginWrite来实现。根据你的问题
文档不清楚(对我而言) 关于何时回调方法 调用。
在网络驱动程序将数据读入缓冲区后进行调用。
它是否基本上立即发生, 然后在另一个线程上 EndWrite上的块等待 实际的写作会发生吗?
不,直到它在网络驱动程序处理的缓冲区中。
当某种情况发生时会发生吗? 套接字API中的低级缓冲区 下溢?
如果通过下流你的意思是它有空间,那么是。
数据发生时是否会发生这种情况 实际写到网络?
没有
它是否曾经发生过 确认?
没有
修改强>
我个人会尝试使用Thread。 BeginWrite在幕后做了很多你应该认识的东西......加上我很奇怪,我喜欢控制我的线程。