避免在C#中发生递归事件

Avoid recursive event in c#
2021-06-10
  •  译文(汉语)
  •  原文(英语)

嗨,我有一个小问题,我正在使用套接字,并且发现了许多无限递归的情况,请参见源:http : //pastebin.com/Cbd2Z2uE

问题是这样的:

private static void ReceiveCallback(IAsyncResult ar)
{
    ....
    // receive again
    socket.BeginReceive(state.Buffer,
        0,
        StateObject.BufferSize,
        0,
        ReceiveCallback,
    state);

    ....
}

因此,我们有一个异步函数,该函数以递归方式再次被调用.在这里,我们对堆栈有疑问吗?更明确地说,这种情况还可以,递归很好,问题是:在这种情况下,我可以遇到stackoverflow问题吗?

谢谢

速聊1:
我没有看您的情况,但是当面对递归时,通常的解决方案是以某种方式停止递归(停止条件).或在诸如电话b和b呼叫a的情况下.然后在共享逻辑为时引入第三分量c.
速聊2:
谢谢,但不是递归的问题,问题是在使用套接字的这种"常见"情况下,我是否冒着stackoverflow的风险,如果是,则如何避免它:)
速聊3:
这与涉及递归的任何情况没有什么不同-您有一些条件,如果为true,则调用递归,如果为false,则不递归.
速聊4:
我建议不要使用BeginReceive,而是为每个将阻塞并等待的套接字创建一个线程Receive.
速聊5:
看起来您根本没有重复发生,您正在链接异步调用(调用->回调,新调用->回调...),因此应该没有溢出问题.
解决过程1

您没有递归(或处于危险或递归中).您只是在计划要在IO竞争端口接收到要处理的数据时执行的回调(已检查源代码以查看此信息).当您的函数在当前线程上运行时,这不会发生,因为同一线程必须通过轮询来检查IO端口上的消息,否则另一个工作线程将一起运行您的函数.无论哪种情况,您都不会递归或处于缩回的危险中,因此堆栈不会崩溃(socket.BeginReceive应立即返回并不会在其中调用函数).

我相信您正在看的是此页面上显示的方案14,但它可能是以前的方案,因为您的控制台应用程序具有消息泵.但最重要的是,您的应用程序中没有递归的实际功能(或支持应用程序使用的OS框架功能).

速聊1:
因此,如果我很了解:如果我有一个事件或对简单Action的调用,那么在这种情况下我不会递归,对吗?
速聊2:
您的情况就是所谓的"调用事件处理程序",因此,通常情况下(如果操作正确),您不会递归.动作是对函数指针(代表C#的委派)的高级抽象,因此即使它具有某些相关性,我也不会对此发表评论.但是要进行递归,您必须在自身内部调用相同的函数,以便在您的示例中创建递归:如果您在ReceiveCallback方法主体的(内部)这样再次调用它, ReceiveCallback(null)您将看到递归
解决过程2

ReceiveCallback是通过框架代码,除非你直接打电话叫. BeginReceive将指定的委托存储为回调函数,并且此时不调用委托.

例如,我添加了一些代码来获取堆栈跟踪:

private static void ReceiveCallback(IAsyncResult ar)
{
    Console.WriteLine(Environment.StackTrace);
    // retrieve the state and socket

并得到:

at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at Program.ReceiveCallback(IAsyncResult ar) in r:\Temp\LINQPad\aqwfvqfb\query_ettlka.cs:line 102
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

而且即使您拨入BeginReceive,它也会显示出来ReceiveCallback.

Hi I 've a little question, I'm using socket and I find many situation of infinite recursion see source: http://pastebin.com/Cbd2Z2uE

the problem is this:

private static void ReceiveCallback(IAsyncResult ar)
{
    ....
    // receive again
    socket.BeginReceive(state.Buffer,
        0,
        StateObject.BufferSize,
        0,
        ReceiveCallback,
    state);

    ....
}

So we have an async function that is called again in a recursive way. Here We have a problem with the stack? To be more clear, this situation is ok and recursion is fine, the problem and question is: in this case can I have a problem of stackoverflow?

thanks

Talk1:
I havent look at your situation, but when facing recursion, often the solution is to somehow stop recursion (stop condition) . Or in situation like a calls b and b calls a. Then introduce a thrid component c when the shared logic is.
Talk2:
thanks but is not the problem of recursion here, the question is in this "common" situation using socket I risk a stackoverflow or not, and if yes how avoid it :)
Talk3:
This is no different from any situation involving recursion - you have some condition, that if true, calls the recursion, and if false, it doesn't.
Talk4:
I suggest not using BeginReceive but instead creating a thread for each socket that will block and wait on Receive.
Talk5:
Looks like you're not recurring at all, you're chaining async calls (call -> callback, new call -> callback ...), so there should be no overflow issues.
Solutions1

You are not recursing (or in danger or recursing). You are simply scheduling a callback to be executed when an IO competion port receives data for you to process (checked source code to see this). This will not happen while your function is running on the current thread because the very same thread has to check for message on IO port via polling or another worker thread will run your function all together. In either case you are not recursing or in danger of recusing so the stack will not blow up (socket.BeginReceive should return immediatly & will not call your function in it).

I believe you are looking at scenario 14 shown on this page but it may be the previous scenario since your console app has a message pump. But the bottom line is that there is no actual functions recursing in your application (or supporitng OS framework functionality used by it).

Talk1:
So if I well understand: if I have an event or a call to simple Action I'm recursing in this case not, right?
Talk2:
Your situation is what you may call 'calling event handler', so in case of events usually (if done right) you are not recursing. Action is a high level abstraction over function pointers (delegates as known C#) so I'll not comment on that even though it has some relevance. But to recurse you have to call the same function within itself, so to create a recursion in you example: if you in (inside) the body of the ReceiveCallback method call it again like this ReceiveCallback(null) you will see recursion
Solutions2

Your ReceiveCallback is called by framework code unless you call it directly. BeginReceive stores specified delegate as callback function, and it doesn't calls the delegate at that time.

For example, I added some codes to get stack trace:

private static void ReceiveCallback(IAsyncResult ar)
{
    Console.WriteLine(Environment.StackTrace);
    // retrieve the state and socket

and gets:

at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Environment.get_StackTrace()
at Program.ReceiveCallback(IAsyncResult ar) in r:\Temp\LINQPad\aqwfvqfb\query_ettlka.cs:line 102
at System.Net.LazyAsyncResult.Complete(IntPtr userToken)
at System.Net.ContextAwareResult.CompleteCallback(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Net.ContextAwareResult.Complete(IntPtr userToken)
at System.Net.LazyAsyncResult.ProtectedInvokeCallback(Object result, IntPtr userToken)
at System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

And It's shown even you call BeginReceive in ReceiveCallback.

转载于:https://stackoverflow.com/questions/19063949/avoid-recursive-event-in-c-sharp

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入