0

Suppose I have a recursive function called bool IsMagicNumber(int n) that returns if n is a magic number or not.

If the function takes more than 3 seconds to verify if n is a magic number, it means n is not a magic number and I want to abort the function immediatly and start testing the next number.

My situation is something like this:

for(int i = 0; i < 1000; i++){
    Console.WriteLine(IsMagicNumber(i));
}

Preprocessing results is not an option since IsMagicNumber can return different values for the same input.

How can I modify my code to abort the execution of IsMagicNumber(i) for an i that is taking too long (and just print false and move to i + 1)?

Daniel
  • 7,357
  • 7
  • 32
  • 84
  • 1
    "Abort" as forceful termination from outside (impossible to do correctly) or as "cancel cooperatively" (i.e. using CancellationToken)? – Alexei Levenkov Mar 14 '23 at 22:56
  • Can you run `IsMagicNumber` in a background `Task` ? Then you can simply cancel the Task after 3 seconds. – Poul Bak Mar 14 '23 at 22:57
  • 1
    i would pass the time limit to the function and have it check every now and then – pm100 Mar 14 '23 at 23:01
  • 1
    @PoulBak there is no such thing as "simply cancel task" - task **must** cooperate and check cancellation token once in a while anyway... So really no gain in running as separate task. – Alexei Levenkov Mar 14 '23 at 23:02
  • Is using Task the best option I've got? I'm not so familiar so I will give it a look. – Daniel Mar 14 '23 at 23:04
  • I think if you are looking for cooperative cancellation (not aborting the code forcefully) than @pm100 suggestion (implemented either directly with TimeSpan or I'd use CancellationToken like shown in https://stackoverflow.com/questions/25263082/how-to-cancel-a-taskcompletionsource-using-a-timeout since it already wraps time checking code) is the one you should go with. There is no benefit I can see from using Task or starting code on separate thread. – Alexei Levenkov Mar 14 '23 at 23:08
  • [Here you go](https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/cancel-async-tasks-after-a-period-of-time#complete-example) – Pieterjan Mar 14 '23 at 23:36

2 Answers2

0

I think you really have three options.

Use a timeout parameter, combined with the repeated timeout check inside your function (here I'm using a Stopwatch to achieve this):

for (var i = 0; i < 1000; i++)
{
    Console.WriteLine(IsMagicNumber(i, TimeSpan.FromSeconds(3)));
}

bool IsMagicNumber(int i, TimeSpan timeout)
{
    var stopwatch = Stopwatch.StartNew();

    // Somewhere in your algorithm, presumably inside a loop:
    if (stopwatch.Elapsed >= timeout)
    {
        return false;
    }
}

Use a cancellation token, which will throw an OperationCanceledException after 3 seconds (here I'm using a CancellationTokenSource with the delay parameter of 3000 milliseconds):

for (var i = 0; i < 1000; i++)
{
    try
    {
        using var timeoutTokenSource = new CancellationTokenSource(3000);
        Console.WriteLine(IsMagicNumber(i, timeoutTokenSource.Token));
    }
    catch (OperationCanceledException)
    {
        // Continue.
    }
}

bool IsMagicNumber(int i, CancellationToken cancellationToken)
{
    // Somewhere in your algorithm, presumably inside a loop:
    cancellationToken.ThrowIfCancellationRequested();
}

Not recommended: Run your function in a separate thread, then use Thread.Abort to kill it, if you really have no other options. This will only work on the "classic" .NET Framework:

var thread = new Thread(p => IsMagicNumber((int)p));

for (var i = 0; i < 1000; i++)
{
    thread.Start(i);

    if (!thread.Join(3000))
    {
        thread.Abort();
    }
}
Yarik
  • 1,423
  • 1
  • 14
  • 16
  • 1
    I would upvote if you removed the `Abort` section. Even though you've said "Not recommended" it is still encouraging the use of `Abort`. – Enigmativity Mar 15 '23 at 00:07
  • @Enigmativity It would be unfair to not mention it. And I don't get how "not recommended" in bold could be in any way encouraging. – Yarik Mar 16 '23 at 00:28
-2

Using Thread Library

I found a sample for creating thread and writed this code, hope this helps

ExampleThread exampleThread = new ExampleThread();

Thread myThread = new Thread(new ThreadStart(exampleThread.thread));

myThread.Start();

Thread.Sleep(1000); // 1 second

if (myThread.IsAlive)
    myThread.Abort();

class ExampleThread
{
    public void thread()
    {
        for (int i = 0; i < 100_000; i++)
        {
            Console.WriteLine($"Hello : {i}");
        }
    }
}

For this loop when 1 second ended thread aborted and last loop output is:

Hello : 79490

So you can abort thread if it is running too long (you can decide how long with milliseconds, 1000 milliseconds = 1 second)

HBA114
  • 17
  • 5
  • 1
    Never ever ever abort a thread. It's dangerous and can corrupt the run-time so you cannot rely on the running code even in the remaining threads. – Enigmativity Mar 15 '23 at 00:08