如何用超时包装恶意功能?

时间:2019-04-27 22:30:56

标签: c# task

我在第3方库中有一个函数,该函数有时会变得无赖,再也不会返回。像这样:

    // This is 3rd party function so I can't make it take a cancellation token.
    public void RogueFunction()
    {

        while (true)
        {
            _logger.LogInformation("sleeping...");
            Thread.Sleep(100);
        }
    }

我想将其包装在一个具有超时的任务中,这很容易通过“ task.Wait(mills)”来完成。超时后,这将控制权交还给我,但实际上并没有终止任务。

在下面的代码中,流氓功能在超时后继续记录。

    [Fact]
    public void Test()
    {
        var task = Task.Factory.StartNew(RogueFunction);
        var complete = task.Wait(500);
        if (!complete)
        {
            // how do I kill the task so that it quits logging?
            Thread.Sleep(5000);

            task.Dispose();  // Throws exception: A task may only be disposed if it is in a completion state (RanToCompletion, Faulted or Canceled).
        }
    }

我如何完全终止此任务,以便我可以重试它而不会最终使它们中的一堆在后台线程池中无限运行。

2 个答案:

答案 0 :(得分:2)

似乎Thread.Abort是您唯一的选择。如果您担心这样做可能会使应用程序处于损坏状态(打开文件句柄等),那么最安全的选择是在另一个进程中运行线程,然后运行kill the process。另一个可行的解决方案是在另一个AppDomain中运行线程,然后中止线程和unload the AppDomain

答案 1 :(得分:1)

已更新

让我们具有这样的功能:

static class Rogue
{
    // This is 3rd party function so I can't make it take a cancellation token.
    public static void RogueFunction()
    {
        while (true)
        {
            Console.WriteLine("RogueFunction works");
            Thread.Sleep(1000);
        }
    }
}

可能的解决方案是用这样的类包装它:

public class InfiniteAction
{
    private readonly Action action;
    private CancellationTokenSource cts;
    private Thread thread;

    public InfiniteAction(Action action) => this.action = action;

    public void Join() => thread?.Join();

    public void Start()
    {
        if (cts == null)
        {
            cts = new CancellationTokenSource();
            thread = new Thread(() => action());
            thread.IsBackground = true;
            thread.Start();
            cts.Token.Register(thread.Abort);
        }
    }

    public void Stop()
    {
        if (cts != null)
        {
            cts.Cancel();
            cts.Dispose();
            cts = null;
        }
    }
}

现在,人们可以像InfiniteAction.Start()这样开始无限行动,而像InfiniteAction.Stop()这样来停止它。

可以手动完成:

void ManualCancelation()
{
    var infiniteAction = new InfiniteAction(Rogue.RogueFunction);
    Console.WriteLine("RogueFunction is executing.");
    infiniteAction.Start();

    Console.WriteLine("Press any key to stop it.");
    Console.ReadKey();
    Console.WriteLine();
    infiniteAction.Stop();

    Console.WriteLine("Make sure it has stopped and press any key to exit.");
    Console.ReadKey();
    Console.WriteLine();
}

或通过计时器:

void ByTimerCancelation()
{
    var interval = 3000;
    var infiniteAction = new InfiniteAction(Rogue.RogueFunction);
    Console.WriteLine($"RogueFunction is executing and will be stopped in {interval} ms.");
    Console.WriteLine("Make sure it has stopped and press any key to exit.");
    infiniteAction.Start();
    var timer = new Timer(StopInfiniteAction, infiniteAction, interval, -1);
    Console.ReadKey();
    Console.WriteLine();
}

private void StopInfiniteAction(object action)
{
    var infiniteAction = action as InfiniteAction;
    if (infiniteAction != null)
        infiniteAction.Stop();
    else
        throw new ArgumentException($"Invalid argument {nameof(action)}");
}