ManualResetEvent会阻塞整个程序吗?

本文关键字:程序 ManualResetEvent | 更新日期: 2023-09-27 17:49:30

我有一个程序,它从侦听连接开始。我想实现一种模式,在这种模式中,服务器接受连接,将单个连接传递给用户类进行处理:未来的数据包接收和数据处理。

在发现异步使用Socket类并不可怕之前,我在使用同步模式时遇到了麻烦。但后来我遇到了更多的麻烦。看起来,在while (true)循环中,由于BeginAccept()是异步的,程序将不断地通过这个循环并最终运行到OutOfMemoryException。我需要一些东西来监听连接,并立即将该连接的责任移交给其他类。

所以我读了微软的例子,发现了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));
}

ManualResetEvent会阻塞整个程序吗?

微软的例子,他们使用老式的基于WaitHandle的事件,将工作,但坦率地说,这是一个非常奇怪和笨拙的方式来实现异步代码。我有一种感觉,这个例子中的事件主要是作为一种人为同步主线程的方式,所以它有一些事情要做。但这并不是正确的方法。

一种选择是甚至不异步接受套接字。相反,在套接字连接时使用异步I/O,并在主线程中使用同步循环来接受套接字。这与微软的示例所做的差不多,但是将所有的accept逻辑保持在主线程中,而不是在主线程(开始accept操作)和处理完成的IOCP线程之间来回切换。

另一个选择是给主线程一些其他的事情去做。举一个简单的例子,这可能只是等待一些用户输入来表示程序应该关闭。当然,在真实的程序中,主线程可能是有用的(例如,在GUI程序中处理消息循环)。

如果主线程有其他事情要做,那么你可以按照预期的方式使用异步BeginAccept():你调用该方法来启动接受操作,然后在该操作完成之前不要再调用它。初始化调用发生在初始化服务器时,但所有后续调用都发生在完成回调中。

在这种情况下,你的完成回调方法看起来更像这样:

static void ConnectionAccepted(IAsyncResult IAr)
{
    Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
    MainSocket.BeginAccept(ConnectionAccepted, MainSocket);
}

也就是说,您只需在完成回调本身中调用BeginAccept()方法。(注意,不需要显式地创建AsyncCallback对象;编译器将隐式地将方法名转换为代表您的正确委托类型实例。