使用AsyncEnumerator实现APM模式时的异常处理
本文关键字:异常处理 模式 APM AsyncEnumerator 实现 使用 | 更新日期: 2023-09-27 18:06:12
我正在尝试使用Richter的AsyncEnumerator
类实现APM模式。目标是实现一个ExtendedSocket
类,它来源于Socket
,并提供Begin/EndReceiveFixed
和Begin/EndSendFixed
方法来异步发送或接收固定数量的字节。
代码看起来像这样(我省略了发送部分,因为它基本上与接收相同):
class ExtendedSocket : Socket
{
public ExtendedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
: base(addressFamily, socketType, protocolType)
{
}
public IAsyncResult BeginReceiveFixed(byte[] buffer, SocketFlags socketFlags, AsyncCallback callback, Object state)
{
AsyncEnumerator ae = new AsyncEnumerator();
return ae.BeginExecute(DoReceiveFixed(ae, buffer, socketFlags), callback, state);
}
public void EndReceiveFixed(IAsyncResult asyncResult)
{
AsyncResult ar = asyncResult as AsyncResult;
(ar.InitiatingObject as AsyncEnumerator).EndExecute(ar);
}
private IEnumerator<Int32> DoReceiveFixed(AsyncEnumerator ae, byte[] buffer, SocketFlags socketFlags)
{
int totalReceivedBytes = 0;
while (totalReceivedBytes < buffer.Length)
{
BeginReceive(buffer, totalReceivedBytes, buffer.Length - totalReceivedBytes, socketFlags, ae.End(), null);
yield return 1;
totalReceivedBytes += EndReceive(ae.DequeueAsyncResult());
}
}
}
这在我的应用程序中非常好,但我不知道如何处理DoReceiveFixed
中的异常。我想实现默认的APM行为,当EndReceiveFixed
被调用时(重新)抛出异常。
不幸的是,我不能访问DoReceiveFixed
内部的AsyncResult
对象,所以我不能在AsyncResult
对象上异常调用SetAsCompleted
。
我目前的解决方法是使用AsyncEnumerator<Exception>
代替AsyncEnumerator
,像这样:
class ExtendedSocket : Socket
{
public ExtendedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
: base(addressFamily, socketType, protocolType)
{
}
public IAsyncResult BeginReceiveFixed(byte[] buffer, SocketFlags socketFlags, AsyncCallback callback, Object state)
{
AsyncEnumerator<Exception> ae = new AsyncEnumerator<Exception>();
return ae.BeginExecute(DoReceiveFixed(ae, buffer, socketFlags), callback, state);
}
public void EndReceiveFixed(IAsyncResult asyncResult)
{
AsyncResult ar = asyncResult as AsyncResult;
AsyncEnumerator<Exception> ae = ar.InitiatingObject as AsyncEnumerator<Exception>;
ae.EndExecute(ar);
if (ae.Result != null)
{
throw ae.Result;
}
}
private IEnumerator<Int32> DoReceiveFixed(AsyncEnumerator<Exception> ae, byte[] buffer, SocketFlags socketFlags)
{
int totalReceivedBytes = 0;
Exception catchedException = null;
while (totalReceivedBytes < buffer.Length)
{
try
{
BeginReceive(buffer, totalReceivedBytes, buffer.Length - totalReceivedBytes, socketFlags, ae.End(), null);
}
catch (Exception ex)
{
catchedException = ex;
break;
}
yield return 1;
try
{
totalReceivedBytes += EndReceive(ae.DequeueAsyncResult());
}
catch (Exception ex)
{
catchedException = ex;
break;
}
}
ae.Result = catchedException;
}
}
这似乎工作,但我不太喜欢这个解决方案。有更好的方法吗?也许有一种方法可以从DoFixedReceive
内部访问AsyncResult
对象?
在Jeffrey Richter的帮助下,我解决了我的问题:
不需要捕获迭代器中的所有异常并手动重新抛出它们。AsyncEnumerator
为我们做了这些。
但是要小心调试器的设置。我需要取消常规调试页面上的"只启用我的代码"设置。否则,如果在迭代器内部发生异常,在AsyncEnumerator
有机会捕获异常之前,调试器就会中断并产生未处理的异常消息。