【问题标题】:Aborting task that contains never-ending library method (no ability of checking cancellation requests)包含永无止境的库方法的中止任务(无法检查取消请求)
【发布时间】:2017-11-04 20:39:22
【问题描述】:

我使用 Pcap.Net 进行流量监控,我需要它来接收数据包,直到用户请求取消。我以这种方式创建监控任务(简化):

var task1 = Task.Run(() => { communicator.ReceivePackets(0, PacketHandlerCallback); } /*, token*/);

这里0 表示ReceivePackets 的执行永远不会结束,PacketHandlerCallback 是一个将为每个接收到的数据包执行的方法。 ReceivePackets 是同步的,不支持取消。一般来说,在我的问题中它可能是我们无法编辑的任何其他无限同步方法

问题是如何停止这个方法的执行?

  • 仅将取消令牌传递给任务并没有帮助,因为我们还应该明确检查是否请求取消,例如。 G。致电token.throwIfCancellationRequested()

  • 将令牌传递给回调方法也不是解决方案,因为在收到新数据包之前不会调用此方法,但我想在取消后立即停止我的任务。

  • 使用BackgroundWorker 会导致同样的问题,因为我们应该检查CancellationPending

  • 创建定期检查取消请求的 task2 然后写入 var task = Task.WhenAny(task1, task2) 没有帮助,因为 ReceivePackets 仍将执行

我应该使用Thread.Abort() 还是有其他优雅的解决方案?
SO上有关于TPL的类似问题,但我找不到任何简单而有用的答案。

【问题讨论】:

  • 您可能需要考虑设置communicator.NonBlocking = true,然后重写您的代码以在非阻塞模式下使用communicator.ReceiveSomePackets,这样您就不必担心代码挂了,您可以当你想把所有东西都清理干净的时候,把通讯器扔掉。
  • @Scott 很好的建议,谢谢。我知道这不是问题所在,但这正是我现在需要的。您能否确认如果我为非阻塞通信器写while(true) { communicator.ReceiveSomePackets(/*args*/); token.throwIfCancellationRequested(); },我不会丢失在取消请求检查期间可以接收到的数据包?
  • 以前从未使用过该库,我只是检查了来源并看到了该属性和该属性的注释。

标签: c# asynchronous task-parallel-library cancellation pcap.net


【解决方案1】:

在无法取消的情况下,比 Thread.Abort 更好的解决方案是将不可取消的代码放入可以终止的单独进程中。

这可以保证释放线程持有的所有资源,因为操作系统将在进程退出时释放任何持有的非托管操作系统资源,如句柄,如果中止线程或使用单独的线程,则不会出现此行为AppDomain 你关闭了。

当您编写了第二个进程时,您可以使用WCF over named pipes 之类的东西,这样您就可以与外部进程进行交互,就像您处理进程内的任何其他正常函数一样。

【讨论】:

    【解决方案2】:

    通常Thread.Abort 真的不应该被用作非常过时和危险的方法。但是,您的情况看起来您必须终止线程/进程才能停止无限方法。

    我建议您避免中止任何线程,因为这会导致系统不稳定,因为中止的线程将无法正确清理资源。您可以run your method in new AppDomain,并在取消请求的情况下卸载该域。此外,正如 Scott 所说,单独的 Process 也是一种解决方案。

    当且仅当,出于某种原因,这不是你的选择,你可以subscribe for cancellation of your token with Thread.CurrentThread.Abort,但如果我是你,我会尽可能避免这个选择。

    您也可以创建一个task from cancellation token 并使用WhenAll 异步等待取消。

    【讨论】:

      【解决方案3】:

      PacketCommunicator.Break() 方法。

      来自文档代码:

      /// <summary>
      /// Set a flag that will force ReceiveSomePackets(), ReceivePackets() or ReceiveStatistics() to return rather than looping.
      /// They will return the number of packets/statistics that have been processed so far, with return value BreakLoop.
      /// <seealso cref="ReceiveSomePackets"/>
      /// <seealso cref="ReceivePackets"/>
      /// <seealso cref="ReceiveStatistics(int, HandleStatistics)"/>
      /// </summary>
      /// <remarks>
      ///   <list type="bullet">
      ///     <item>This routine is safe to use inside a signal handler on UNIX or a console control handler on Windows, as it merely sets a flag that is checked within the loop.</item>
      ///     <item>The flag is checked in loops reading packets from the OS - a signal by itself will not necessarily terminate those loops - as well as in loops processing a set of packets/statistics returned by the OS.</item>
      ///     <item>Note that if you are catching signals on UNIX systems that support restarting system calls after a signal, and calling Break() in the signal handler, you must specify, when catching those signals, that system calls should NOT be restarted by that signal. Otherwise, if the signal interrupted a call reading packets in a live capture, when your signal handler returns after calling Break(), the call will be restarted, and the loop will not terminate until more packets arrive and the call completes.</item>
      ///     <item>ReceivePacket() will, on some platforms, loop reading packets from the OS; that loop will not necessarily be terminated by a signal, so Break() should be used to terminate packet processing even if ReceivePacket() is being used.</item>
      ///     <item>Break() does not guarantee that no further packets/statistics will be processed by ReceiveSomePackets(), ReceivePackets() or ReceiveStatistics() after it is called; at most one more packet might be processed.</item>
      ///     <item>If BreakLoop is returned from ReceiveSomePackets(), ReceivePackets() or ReceiveStatistics(), the flag is cleared, so a subsequent call will resume reading packets. If a different return value is returned, the flag is not cleared, so a subsequent call will return BreakLoop and clear the flag.</item>
      ///   </list>
      /// </remarks>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-11-21
        • 1970-01-01
        • 2011-08-13
        • 1970-01-01
        • 1970-01-01
        • 2022-12-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多