I want to have a code block, which should be executed with a maximum time limit. If the functions hangs, it should be aborted. From this question I adapted the following solution:
public static void ExecuteWithTimeLimit(int timeLimit_milliseconds, Func<bool> codeBlock)
{
Task task = Task.Factory.StartNew(() =>
{
codeBlock();
});
task.Wait(timeLimit_milliseconds);
}
This works as I want it to behave: If the code codeBlock
hangs and takes to long, the task is aborted.
However, I want the Task
to have a return value so I can use task.Result
. If I implement this into the code, it doesn't work any more.
In fact, the task is not cancled and the GUI freezes completly.
public static void ExecuteWithTimeLimit(int timeLimit_milliseconds, Func<bool> codeBlock)
{
Task<bool> task = Task<bool>.Factory.StartNew(() =>
{
return codeBlock();
});
task.Wait(timeLimit_milliseconds);
}
What is the correct way to execute Methods with a return value with a maximum time limit?
CodePudding user response:
There's a lot of mucking around with cancellation tokens with tasks. I'd suggest making your life easier and use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive
and add using System.Reactive.Linq;
- then you can do this:
public static async Task<bool> ExecuteWithTimeLimit(TimeSpan timeLimit, Func<bool> codeBlock)
=> await Observable.Amb(
Observable.Timer(timeLimit).Select(_ => false),
Observable.Start(() => codeBlock()));
Observable.Amb
takes 2 or more observables and only returns values from whichever observable fires first. Observable.Timer
fires a single value after the TimeSpan
provided. Observable.Start
executes what ever code and returns a single value that is the result of that code.
Effectively Amb
is a race between the timer and the code.
Now I can run it like this:
Task<bool> task =
ExecuteWithTimeLimit(TimeSpan.FromSeconds(1.0), () =>
{
Console.WriteLine("!");
Thread.Sleep(TimeSpan.FromSeconds(2.0));
Console.WriteLine("!!");
return true;
});
task.Wait();
Console.WriteLine(task.Result);
When I run that I get this on the console:
!
False
!!
If I change the timeLimit
to TimeSpan.FromSeconds(3.0)
then I get this:
!
!!
True
CodePudding user response:
I would recommend creating a task method and using await. This will release the thread so application doesn't lock up, and once result is available it will jump back into that thread Here is an example:
public async Task MyMethodAsync()
{
Task<string> longRunningTask = LongRunningOperationAsync();
// independent work which doesn't need the result of LongRunningOperationAsync can be done here
//and now we call await on the task
string result = await longRunningTask;
//use the result
Console.WriteLine(result);
}
public async Task<string> LongRunningOperationAsync() // assume we return an int from this long running operation
{
//Perform your task in here
await Task.Delay(5000); // 5 second delay to show how it releases thread
return "Task Complete";
}