在任务中引发事件会导致订阅者方法失败

时间:2017-02-06 22:18:52

标签: c# multithreading parallel-processing task threadpool

我有一些课程是"链接"通过各种活动。基本上是TCPListenerClientSessionServer

TCPListener冒充新连接到Server,然后Server创建一个新的ClientSession

TCPListener通过事件ClientConnected传递新套接字。

引发事件的方法如下所示:

    //Processes a new connection and immediately puts the listen socket back in a receiving state
    private void ProcessAccept(SocketAsyncEventArgs e)
    {
        ClientConnected(this, e));            

        StartAccept();
    }

我想避免做任何可以阻止或减慢接受新客户的事情,所以我努力用一个任务来提升事件。原因是用户可以覆盖OnClientConnected中的Server方法,使其长时间运行并可能影响服务器性能。

以下是修订后的方法:

    //Processes a new connection and immediately puts the listen socket back in a receiving state
    private void ProcessAccept(SocketAsyncEventArgs e)
    {
        Task.Run(() => ClientConnected(this, e));            

        StartAccept();
    }

使用新方法运行程序时,崩溃时没有例外,但服务器无法接收或发送给客户端。

以下是OnClientConnected中的Server方法:

    /// <summary>
    /// An event that is fired when a client connects.
    /// </summary>
    /// <param name="sender">The Listener that accepted the connection</param>
    /// <param name="e">The SocketAsyncEventArgs </param>
    protected virtual void OnClientConnected(object sender, EventArgs e)
    {
        ClientSession Session = ClientSessionPool.Pop();

        Session.Socket = ((SocketAsyncEventArgs)e).AcceptSocket;

        string WelcomeMessage = "Connected";

        Session.SendAsync(Encoding.Default.GetBytes(WelcomeMessage));

        this.ClientSessions.Add(Session);

        Console.Write($"\rConnected clients:{ClientSessions.Count}");
    }

简单地回到旧的同步方法就可以了。

让我烦恼的是,无论是否使用SocketAsyncEventArgs,通过事件传递的Task似乎都是正确的,这是我能想到的唯一失败点

使用SocketAsyncEventArgs版本的方法时,Task似乎完全错误。

我怀疑是我对分配给新线程的堆栈的理解导致了我的困惑。任何人都能看到我的逻辑漏洞吗?谢谢!

PS

我知道我ClientSessions列表的当前实现不是线程安全的,但在我的测试中,我一次只能连接一个客户端。我最终解决这个问题。

PPS 以下是StartAccept方法,以防它有用:

    //Puts the accepting TCP socket back into an accepting state
    public void StartAccept()
    {
        // socket must be cleared since the context object is being reused
        m_SocketEventArgs.AcceptSocket = null;

        bool willRaiseEvent = m_Socket.AcceptAsync(m_SocketEventArgs);
        if (!willRaiseEvent)
        {
            ProcessAccept(m_SocketEventArgs);
        }
    }

1 个答案:

答案 0 :(得分:2)

解决方案是在UI线程上引发事件,但异步运行事件处理。

APPLY