在任务中引发事件会导致订阅者方法失败
Raising an event in a Task causes the subscriber method to fail
我有一些 类 通过各种事件 "linked"。基本上是 TCPListener
、ClientSession
和 Server
.
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();
}
我想避免做任何可能阻止或减慢接受新客户的事情,所以我努力通过任务引发事件。这样做的原因是用户可以覆盖 Server
中的 OnClientConnected
方法,这样它很长 运行ning 并且可能影响服务器性能。
修改后的方法如下:
//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();
}
当程序运行使用新方法时,没有崩溃异常,但服务器无法接收或发送给客户端。
这里是Server
中的OnClientConnected
方法:
/// <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}");
}
只需恢复到旧的同步方法即可。
让我烦恼的是,无论是否使用 Task
,通过事件传递的 SocketAsyncEventArgs
似乎都是正确的,这是唯一的失败点我能想到。
使用 Task
版本的方法时,SocketAsyncEventArgs
似乎完全错误。
我怀疑是我对分配给新线程的堆栈的理解导致了我的困惑。谁能看出我的逻辑有漏洞?谢谢!
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);
}
}
解决方案是在 UI 线程上引发您的事件,但 运行 您的事件处理是异步的。
private async void OnClientConnected(object sender, EventArgs e)
{
await Task.Run(() => HandleClientConnected(object, e));
}
protected abstract Task HandleClientConnected(object sender, EventArgs e);
我有一些 类 通过各种事件 "linked"。基本上是 TCPListener
、ClientSession
和 Server
.
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();
}
我想避免做任何可能阻止或减慢接受新客户的事情,所以我努力通过任务引发事件。这样做的原因是用户可以覆盖 Server
中的 OnClientConnected
方法,这样它很长 运行ning 并且可能影响服务器性能。
修改后的方法如下:
//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();
}
当程序运行使用新方法时,没有崩溃异常,但服务器无法接收或发送给客户端。
这里是Server
中的OnClientConnected
方法:
/// <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}");
}
只需恢复到旧的同步方法即可。
让我烦恼的是,无论是否使用 Task
,通过事件传递的 SocketAsyncEventArgs
似乎都是正确的,这是唯一的失败点我能想到。
使用 Task
版本的方法时,SocketAsyncEventArgs
似乎完全错误。
我怀疑是我对分配给新线程的堆栈的理解导致了我的困惑。谁能看出我的逻辑有漏洞?谢谢!
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);
}
}
解决方案是在 UI 线程上引发您的事件,但 运行 您的事件处理是异步的。
private async void OnClientConnected(object sender, EventArgs e)
{
await Task.Run(() => HandleClientConnected(object, e));
}
protected abstract Task HandleClientConnected(object sender, EventArgs e);