问题
I've got a function in a 3rd party library that occasionally goes rogue and never returns. Something like so:
// 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);
}
}
I'd like to wrap it in a task with a timeout which is easy enough to do with a 'task.Wait(mills)'. While this returns control to me after the timeout, it doesn't actually kill the task.
In the code below, the rogue function continues to log after the timeout.
[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).
}
}
How do I completely kill this task, so that I can retry it without ending up with a bunch of them running infinitely on my background thread pool.
回答1:
It seems that Thread.Abort is your only option. If you are afraid that doing so may leave the application in a corrupted state (open file handles etc), then the safest option is to run the thread in a different process, and then kill the process. Another workable solution is to run the thread in a different AppDomain, and then abort the thread and unload the AppDomain.
回答2:
UPDATED:
Let one has such a function:
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);
}
}
}
Possible solution is to wrap it with a class like this:
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;
}
}
}
Now one can start an infinite action like InfiniteAction.Start()
and stop it like InfiniteAction.Stop()
.
It could be done manually:
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();
}
Or by timer:
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)}");
}
来源:https://stackoverflow.com/questions/55885426/how-do-i-wrap-a-rogue-function-with-a-timeout