ManualResetEvent 会阻塞整个程序吗?
Will ManualResetEvent block the entire program?
我有一个程序,它通过监听连接开始。我想实现一种模式,其中服务器将接受一个连接,将该连接传递给用户 class 以进行处理:未来的数据包接收和数据处理。
在我发现 Socket
class 的异步使用并不可怕之前,我 运行 遇到了同步模式的麻烦。但是后来我运行陷入了更大的麻烦。看起来,在 while (true)
循环中,由于 BeginAccept()
是异步的,程序将不断地通过这个循环并最终 运行 进入 OutOfMemoryException
。我需要一些东西来监听连接,并立即将该连接的责任移交给其他人 class.
所以我阅读了 Microsoft 的示例并了解了 ManualResetEvent
。我实际上可以指定何时准备好让循环再次开始收听!但是在阅读了 Stack Overflow 上的一些问题后,我变得很困惑。
我担心的是,即使我已经异步接受连接,整个程序在重新进入循环时尝试侦听新连接时也会阻塞。如果我要处理多个用户,这并不理想。
我是异步世界的新手 I/O,所以即使是对我的词汇或短语的滥用的最愤怒的评论,我也会很感激。
代码:
static void Main(string[] args)
{
MainSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
MainSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.74"), 1626));
MainSocket.Listen(10);
while (true)
{
Ready.Reset();
AcceptCallback = new AsyncCallback(ConnectionAccepted);
MainSocket.BeginAccept(AcceptCallback, MainSocket);
Ready.WaitOne();
}
}
static void ConnectionAccepted(IAsyncResult IAr)
{
Ready.Set();
Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
}
Microsoft 示例,其中他们使用基于旧式 WaitHandle
的事件,将起作用,但坦率地说,这是实现异步代码的一种非常奇怪和笨拙的方式。我觉得例子中的事件主要是作为一种人为同步主线程的方式,所以它有事情要做。但这并不是真正正确的方法。
一种选择是甚至不接受异步套接字。相反,在连接套接字时使用异步 I/O 并在主线程中使用同步循环来接受套接字。这最终与 Microsoft 示例所做的几乎完全相同,但是将所有接受逻辑保留在主线程中,而不是在主线程(启动接受操作)和一些处理完成的 IOCP 线程之间来回切换.
另一种选择是让主线程做一些其他事情。举一个简单的例子,这可能只是等待一些用户输入来发出程序应该关闭的信号。当然,在实际程序中,主线程可能是有用的(例如处理 GUI 程序中的消息循环)。
如果主线程有其他事情要做,那么你可以按照预期的方式使用异步BeginAccept()
:你调用方法开始接受操作,然后不要调用直到该操作完成。初始调用发生在您初始化服务器时,但所有后续调用都发生在完成回调中。
在那种情况下,您的完成回调方法看起来更像这样:
static void ConnectionAccepted(IAsyncResult IAr)
{
Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
MainSocket.BeginAccept(ConnectionAccepted, MainSocket);
}
也就是说,您只需在完成回调本身中调用 BeginAccept()
方法。 (请注意,无需显式创建 AsyncCallback
对象;编译器会代表您将方法名称隐式转换为正确的委托类型实例)。
我有一个程序,它通过监听连接开始。我想实现一种模式,其中服务器将接受一个连接,将该连接传递给用户 class 以进行处理:未来的数据包接收和数据处理。
在我发现 Socket
class 的异步使用并不可怕之前,我 运行 遇到了同步模式的麻烦。但是后来我运行陷入了更大的麻烦。看起来,在 while (true)
循环中,由于 BeginAccept()
是异步的,程序将不断地通过这个循环并最终 运行 进入 OutOfMemoryException
。我需要一些东西来监听连接,并立即将该连接的责任移交给其他人 class.
所以我阅读了 Microsoft 的示例并了解了 ManualResetEvent
。我实际上可以指定何时准备好让循环再次开始收听!但是在阅读了 Stack Overflow 上的一些问题后,我变得很困惑。
我担心的是,即使我已经异步接受连接,整个程序在重新进入循环时尝试侦听新连接时也会阻塞。如果我要处理多个用户,这并不理想。
我是异步世界的新手 I/O,所以即使是对我的词汇或短语的滥用的最愤怒的评论,我也会很感激。
代码:
static void Main(string[] args)
{
MainSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
MainSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.74"), 1626));
MainSocket.Listen(10);
while (true)
{
Ready.Reset();
AcceptCallback = new AsyncCallback(ConnectionAccepted);
MainSocket.BeginAccept(AcceptCallback, MainSocket);
Ready.WaitOne();
}
}
static void ConnectionAccepted(IAsyncResult IAr)
{
Ready.Set();
Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
}
Microsoft 示例,其中他们使用基于旧式 WaitHandle
的事件,将起作用,但坦率地说,这是实现异步代码的一种非常奇怪和笨拙的方式。我觉得例子中的事件主要是作为一种人为同步主线程的方式,所以它有事情要做。但这并不是真正正确的方法。
一种选择是甚至不接受异步套接字。相反,在连接套接字时使用异步 I/O 并在主线程中使用同步循环来接受套接字。这最终与 Microsoft 示例所做的几乎完全相同,但是将所有接受逻辑保留在主线程中,而不是在主线程(启动接受操作)和一些处理完成的 IOCP 线程之间来回切换.
另一种选择是让主线程做一些其他事情。举一个简单的例子,这可能只是等待一些用户输入来发出程序应该关闭的信号。当然,在实际程序中,主线程可能是有用的(例如处理 GUI 程序中的消息循环)。
如果主线程有其他事情要做,那么你可以按照预期的方式使用异步BeginAccept()
:你调用方法开始接受操作,然后不要调用直到该操作完成。初始调用发生在您初始化服务器时,但所有后续调用都发生在完成回调中。
在那种情况下,您的完成回调方法看起来更像这样:
static void ConnectionAccepted(IAsyncResult IAr)
{
Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
MainSocket.BeginAccept(ConnectionAccepted, MainSocket);
}
也就是说,您只需在完成回调本身中调用 BeginAccept()
方法。 (请注意,无需显式创建 AsyncCallback
对象;编译器会代表您将方法名称隐式转换为正确的委托类型实例)。