2009-07-10 70 views
1

我正在用C#编写一个服务器,它创建一个(很长,甚至可能是无限)IEnumerable<Result>以响应客户端请求,然后将这些结果传回给客户端。让客户端提取数据

我可以设置它,以便如果客户端读取速度很慢(或者一次完全可能几秒钟不读),服务器将不需要等待缓冲区空间的线程停止清除这样它可以拉动下一对Result,将它们序列化,并将它们填充到网络中?

这是如何NetworkStream.BeginWrite的作品?关于何时调用回调方法,文档不清楚(对我而言)。这是否基本上立即发生,只是在另一个线程,然后阻止EndWrite等待实际的写作发生?当套接字API下溢中的某种较低级缓冲区发生时会发生吗?数据实际写入网络时会发生吗?它在被确认后会发生吗?

我很困惑,所以有可能整个问题都是离题的。如果是这样,你能转过来指点我正确的方向来解决我所期望的是一个相当普遍的问题吗?

回答

0

首先,您的主线程可以在其他工作完成时继续执行的唯一方法是使用另一个线程。一个线程不能同时做两件事。

但是,我认为你想避免的是与Thread对象混淆,并且可以通过使用BeginWrite。根据你的问题

的文档是不清楚(我) 什么时候回调方法将 调用。

调用是在网络驱动程序将数据读入缓冲区后进行的。

是否发生根本立即 只是在另一个线程,然后在EndWrite 块等待 实际写入的情况发生?

不,直到它处于由网络驱动程序处理的缓冲区中。

是否发生时,某种在套接字API 下溢 下级缓冲区?

如果通过下流你的意思它有空间,那么是的。

当数据已被实际写入网络时,会发生这种情况吗?

时是否已 承认这一点?

No.

编辑

个人而言,我会尝试使用一个线程。 BeginWrite在幕后做了很多事情,你应该认识到......加上我很奇怪,我喜欢控制我的线程。

+0

这个问题中有一些信息不太准确。例如,正如我在我的文章中所解释的那样,只要数据到达发送缓冲区,回调并不总是被调用。这取决于实施,但即使在Windows上,也取决于IO完成端口是否可用。有时可能会立即调用它并阻止EndWrite。我还会补充说,为每一次发送创建一个线程是不明智的。至少,使用线程池。但是,这只是让事情变得复杂,而且最有可能的做法比BeginWrite的效率低。 – IRBMe 2009-07-12 09:08:28

2

我会更详细地回答你问题的第三部分。

MSDN文档指出:

当应用程序调用BeginWrite,该系统采用一个单独的线程来执行指定的回调方法,并在EndWrite直到的NetworkStream块发送请求的字节数或引发一个例外。

据我了解,在调用BeginSend后是否立即调用回调方法取决于底层实现和平台。例如,如果IO完成端口在Windows上可用,则不会。线程池中的线程在调用之前将会阻塞。

事实上,NetworkStream的BeginWrite方法只是在我的.Net实现中调用底层套接字的BeginSend方法。 Mine在完成端口的情况下使用基础WSASend Winsock函数。这使得它比仅仅为每个发送/写入操作创建自己的线程效率更高,即使您要使用线程池。

如果WSASend的结果是IOPending,Socket.BeginSend方法然后调用OverlappedAsyncResult.CheckAsyncCallOverlappedResult方法,该方法又调用本地RegisterWaitForSingleObject Win32函数。这将导致线程池中的一个线程阻塞,直到WSASend方法指示它已完成,然后调用回调方法。

由NetworkStream.EndSend调用的Socket.EndSend方法将等待发送操作完成。它必须这样做的原因是因为如果IO完成端口不可用,那么将立即调用回调方法。

我必须再次强调这些细节是特定于我的.Net和我的平台的实现,但这应该有希望给你一些见解。