中止包含永无止境的库方法的任务(无法检查取消请求)

时间:2017-06-03 15:51:54

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

我使用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()还是有其他优雅的解决方案? 有关TPL的类似问题,但我找不到任何简单而有用的答案。

3 个答案:

答案 0 :(得分:1)

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

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

当且仅当因为某些原因这不适合你时,你可以subscribe for cancellation of your token with Thread.CurrentThread.Abort,但如果我是你,我会尽可能地避免使用这个选项。

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

答案 1 :(得分:1)

在你无法取消比Thread.Abort更好的解决方案的情况下,将不可取消的代码放入一个单独的进程中就可以杀死。

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

当您编写第二个流程时,您可以使用类似WCF over named pipes的内容,这样您就可以像处理流程中的任何其他常规功能一样与外部流程进行交互。

答案 2 :(得分:1)

There's PacketCommunicator.Break() method.

From the documentation code:

/// <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>