【问题标题】:Will ManualResetEvent block the entire program?ManualResetEvent 会阻塞整个程序吗?
【发布时间】:2015-05-16 04:07:26
【问题描述】:

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

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

所以我阅读了微软的例子,发现了ManualResetEvent。我实际上可以指定何时准备好让循环再次开始收听!但是在阅读了关于 Stack Overflow 的一些问题后,我变得很困惑。

我担心的是,即使我已经异步接受了一个连接,整个程序在重新进入循环时尝试侦听新连接时会阻塞。如果我要处理多个用户,这并不理想。

我对异步 I/O 的世界非常陌生,所以即使是最愤怒的 cmets 对我的词汇或短语的误用我都会感激不尽。

代码:

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));
}

【问题讨论】:

    标签: c# multithreading sockets asynchronous manualresetevent


    【解决方案1】:

    微软的例子中,他们使用了老式的基于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 对象;编译器会代表您将方法名称隐式转换为正确的委托类型实例)。

    【讨论】:

    • 我最喜欢选项一,但我不能拥有 Socket.Accept();撑起整个程序。不会吗?
    • 使用同步 Accept() 的行为与 Microsoft 所示的基于事件的实现完全相同,是的,这意味着它会导致 该线程 阻塞。如果您希望该线程执行其他操作,请按照我的描述使用BeginAccept();即不要像 Microsoft 示例中那样使用阻塞循环,而是在您希望服务器启动时调用 BeginAccept(),让主线程继续执行您希望它执行的任何操作,然后调用 @987654329 @ 再次在您的 ConnectionAccepted() 方法中。
    • 这会导致应用程序退出。我该如何补救?
    • 这没有任何意义。您表达了对“阻止整个程序”的担忧(我假设您只是指主线程......一个线程本身不能阻止任何其他线程)。重要的是它是否支持主线程,这意味着主线程应该在做其他事情。但如果它正在做其他事情,程序将不会退出。你的两个担忧是相互排斥的。如果您不希望主线程退出并且没有更好的事情要做,只需使用同步Accept()
    • 您的建议无效。当我使用它时,只有一个人可以连接。我需要多人连接,并且只要套接字处于活动状态,他们就需要能够以个人身份与游戏互动。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    相关资源
    最近更新 更多