Home > database >  C# - Wait for Task with return value
C# - Wait for Task with return value

Time:12-31

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";
}
  • Related