In an answer to one of my other questions, I was told that use of new Task(() => { }) is not something that is a normal use case. I was advised to use Func<Task> instead. I have tried to make that work, but I can't seem to figure it out. (Rather than drag it out in the comments, I am asking a separate question here.)
My specific scenario is that I need the Task to not start right when it is declared and to be able to wait for it later.
Here is a LinqPad example using new Task(() => { }). NOTE: This works perfectly! (Except that it uses new Task.)
static async void Main(string[] args)
{
// Line that I need to swap to a Func<Task> somehow.
// note that this is "cold" not started task
Task startupDone = new Task(() => { });
var runTask = DoStuff(() =>
{
//+++ This is where we want to task to "start"
startupDone.Start();
});
//+++ Here we wait for the task to possibly start and finish. Or timeout.
// Note that this times out at 1000ms even if "blocking = 10000" below.
var didStartup = startupDone.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
await runTask;
Console.Read();
}
public static async Task DoStuff(Action action)
{
// Swap to 1000 to simulate starting up blocking
var blocking = 1; //1000;
await Task.Delay(500 + blocking);
action();
// Do the rest of the stuff...
await Task.Delay(1000);
}
I tried swapping the second line with:
Func<Task> startupDone = new Func<Task>(async () => { });
But then the lines below the comments with +++ in them don't work right.
I swapped the startupDone.Start() with startupDone.Invoke().
But startupDone.Wait needs the task. Which is only returned in the lambda. I am not sure how to get access to the task outside the lambda so I can Wait for it.
How can use a Func<Task> and start it in one part of my code and do a Wait for it in another part of my code? (Like I can with new Task(() => { })).
The code you posted cannot be refactored to make use of a Func<Task> instead of a cold task, because the method that needs to await the task (the Main method) is not the same method that controls the creation/starting of the task (the lambda parameter of the DoStuff method). This could make the use of the Task constructor legitimate in this case, depending on whether the design decision to delegate the starting of the task to a lambda is justified. In this particular example the startupDone is used as a synchronization primitive, to signal that a condition has been met and the program can continue. This could be achieved equally well by using a specialized synchronization primitive, like for example a SemaphoreSlim:
static async Task Main(string[] args)
{
var startupSemaphore = new SemaphoreSlim(0);
Task runTask = RunAsync(startupSemaphore);
bool startupFinished = await startupSemaphore.WaitAsync(1000);
Console.WriteLine(startupFinished ? "Startup Finished" : "Startup Timed Out");
await runTask;
}
public static async Task RunAsync(SemaphoreSlim startupSemaphore)
{
await Task.Delay(500);
startupSemaphore.Release(); // Signal that the startup is done
await Task.Delay(1000);
}
In my opinion using a SemaphoreSlim is more meaningful in this case, and makes the intent of the code clearer. It also allows to await asynchronously the signal with a timeout WaitAsync(Int32), which is not something that you get from a Task out of the box (it is doable though).
Using cold tasks may be tempting in some cases, but when you revisit your code after a month or two you'll find yourself confused, because of how rare and unexpected is to have to deal with tasks that may or may have not been started yet.
I always try my hardest to never have blocking behavior when dealing with anything async or any type that represents potential async behavior such as Task. You can slightly modify your DoStuff to facilitate waiting on your Action.
static async void Main(string[] args)
{
Func<CancellationToken,Task> startupTask = async(token)=>
{
Console.WriteLine("Waiting");
await Task.Delay(3000, token);
Console.WriteLine("Completed");
};
using var source = new CancellationTokenSource(2000);
var runTask = DoStuff(() => startupTask(source.Token), source.Token);
var didStartup = await runTask;
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
Console.Read();
}
public static async Task<bool> DoStuff(Func<Task> action, CancellationToken token)
{
var blocking = 10000;
try
{
await Task.Delay(500 + blocking, token);
await action();
}
catch(TaskCanceledException ex)
{
return false;
}
await Task.Delay(1000);
return true;
}
First, the type of your "do this later" object is going to become Func<Task>. Then, when the task is started (by invoking the function), you get back a Task that represents the operation:
static async void Main(string[] args)
{
Func<Task> startupDoneDelegate = async () => { };
Task startupDoneTask = null;
var runTask = await DoStuff(() =>
{
startupDoneTask = startupDoneDelegate();
});
var didStartup = startupDoneTask.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
}
Related
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?
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";
}
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
Actually I found a solution by canceling the task after the time limit:
public static void ExecuteWithTimeLimit(int timeLimit_milliseconds, Func<bool> codeBlock)
{
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
Task<bool> task = Task<bool>.Factory.StartNew(() =>
{
try
{
return codeBlock();
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Exeption", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}, cancellationToken);
task.Wait(timeLimit_milliseconds);
cancellationTokenSource.Cancel();
}
I am thinking a way to handle and expensive process which would require me to call and get multiple data from multiple Industrial field. So for each career I will make them into one individual task inside the Generate Report Class. I would like to ask that when using Task.WhenAll is it that task 1,task 2,task 3 will run together without waiting task1 to be completed. But if using Task.WaitAll it will wait for MiningSector to Complete in order to run ChemicalSector. Am I correct since this is what I wanted to achieve.
public static async Task<bool> GenerateReport()
{
static async Task<string> WriteText(string name)
{
var start = DateTime.Now;
Console.WriteLine("Enter {0}, {1}", name, start);
return name;
}
static async Task MiningSector()
{
var task1 = WriteText("task1");
var task2 = WriteText("task2");
var task3 = WriteText("task3");
await Task.WhenAll(task1, task2, task3); //Run All 3 task together
Console.WriteLine("MiningSectorresults: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static async Task ChemicalsSector()
{
var task4 = WriteText("task4");
var task5 = WriteText("task5");
var task6 = WriteText("task6");
await Task.WhenAll(task4 , task5 , task6 ); //Run All 3 task together
Console.WriteLine("ChemicalsSectorresults: {0}", String.Join(", ", t1.Result, t2.Result, t3.Result));
}
static void Main(string[] args)
{
Task.WaitAll(MiningSector(), ChemicalsSector()); //Wait when MiningSectoris complete then start ChemicalsSector.
}
return true;
}
The thing I wanted to achieve is, inside MiningSector have 10 or more functions need to be run and ChemicalSector is same as well. I wanted to run all the functions inside the MiningSector at parallel once it's done then the ChemicalSector will start running the function.
But if using Task.WaitAll it will wait for MiningSector to Complete in order to run ChemicalSector. Am I correct since this is what I wanted to achieve.
No. The key thing to understand here is that arguments are evaluated first, and then the method call. This is the same way all arguments and method calls work across the entire C# language.
So this code:
Task.WaitAll(MiningSector(), ChemicalsSector());
has the same semantics as this code:
var miningTask = MiningSector();
var sectorTask = ChemicalsSector();
Task.WaitAll(miningTask, sectorTask);
In other words, both methods are called (and both tasks started) before Task.WaitAll is called.
If you want to do one then the other, then don't call the second method until the first task has completed. This is most easily done using async Main, e.g.:
static async Task Main(string[] args)
{
await MiningSector();
await ChemicalsSector();
}
I have this code:
var task1 = operation1();
var task2 = operation2();
var result1 = await task1;
var result2 = await task2;
I do also handle UnobservedTaskException (by logging it). The problem that I face is that after task1 fails and first await results in exception, task2 completes in an error and then I have a log entry that I do not want to see as I will already log the first exception and at that point all subsequent exceptions are of no interest to me.
So I am wondering if there is a way to do something so that all tasks are "ignored" in a way after I get an exception.
I know I can use await Task.WhenAll, but the downside is that I have to wait for all exceptions to happen, though I know for sure that after first task results in exception, I don't need to wait for the other task to complete as the whole operation already failed.
Another possible solution is to write try/catch and cancel all tasks, but that's a bit cumbersome.
P.S.
The example is simplified, I do have multiple tasks running like that. So I am looking for a generic solution that would work for any number of tasks
As an alternative to Task.WhenAll you can use a progressive approach with Task.WhenAny.
When any of the Tasks finishes then you can examine the result and you can decide what to do next. (Please bear in mind that Task.WhenAny does not throw exception even it is awaited) The great thing about this approach is that you can easily add throttling (control the degree of parallelism) to this.
The basic implementation of progressive async for each
static async Task ProgressiveAsyncForEach(int degreeOfParallelism, params Task[] tasks)
{
var toBeProcessedTasks = new HashSet<Task>();
var remainingTasksEnumerator = tasks.GetEnumerator();
void AddNextTask()
{
if (!remainingTasksEnumerator.MoveNext()) return;
var nextTaskToProcess = (Task)remainingTasksEnumerator.Current;
toBeProcessedTasks.Add(nextTaskToProcess);
}
//Initialize
while (toBeProcessedTasks.Count < degreeOfParallelism)
{
AddNextTask();
}
while (toBeProcessedTasks.Count > 0)
{
var processedTask = await Task.WhenAny(toBeProcessedTasks).ConfigureAwait(false);
if (!processedTask.IsCompletedSuccessfully)
{
Console.WriteLine("One of the task has failed");
//TODO: log first failed task
//CONSIDER: cancel all the remaining tasks
return;
}
toBeProcessedTasks.Remove(processedTask);
AddNextTask();
}
}
Sample usage
static async Task Main(string[] args)
{
await ProgressiveAsyncForEach(2, Faulty(), Fast(), Slow());
Console.WriteLine("Application finished");
}
static async Task Slow()
{
Console.WriteLine("Slow started");
await Task.Delay(1000);
Console.WriteLine("Slow finished");
}
static async Task Fast()
{
Console.WriteLine("Fast started");
await Task.Delay(500);
Console.WriteLine("Fast finished");
}
static async Task Faulty()
{
Console.WriteLine("Faulty started");
await Task.Delay(700);
throw new Exception();
}
I have long running processing that I want to perform in a background task. At the end of the task, I want to signal that it has completed. So essentially I have two async tasks that I want to run in the background, one after the other.
I am doing this with continuations, but the continuation is starting prior to the initial task completing. The expected behavior is that the continuation run only after the initial task has completed.
Here is some sample code that demonstrates the problem:
// Setup task and continuation
var task = new Task(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
task.Start();
The DebugLog function:
static void DebugLog(string s, params object[] args)
{
string tmp = string.Format(s, args);
System.Diagnostics.Debug.WriteLine("{0}: {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), tmp);
}
Expected Output:
TASK Starting
TASK Finishing
CONTINUATION Starting
CONTINUATION Ending
Actual Output:
TASK Starting
CONTINUATION Starting
CONTINUATION Ending
TASK Finishing
Again, my question is why is the continuation starting prior to the completion of the initial task? How do I get the continuation to run only after the completion of the first task?
Workaround #1
I can make the above code work as expected if I make the intial task synchronous - that is if I Wait on the Task.Delay like so:
var task = new Task(() =>
{
DebugLog("TASK Starting");
Task.Delay(1000).Wait(); // Wait instead of await
DebugLog("TASK Finishing");
});
For many reasons, it is bad to use Wait like this. One reason is that it blocks the thread, and this is something I want to avoid.
Workaround #2
If I take the task creation and move it into it's own function, that seems to work as well:
// START task and setup continuation
var task = Test1();
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
static public async Task Test1()
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
}
Credit for the above approach goes to this somewhat related (but not duplicate) question: Use an async callback with Task.ContinueWith
Workaround #2 is better than Workaround #1 and is likely the approach I'll take if there is no explanation for why my initial code above does not work.
Your original code is not working because the async lambda is being translated into an async void method underneath, which has no built-in way to notify any other code that it has completed. So, what you're seeing is the difference between async void and async Task. This is one of the reasons that you should avoid async void.
That said, if it's possible to do with your code, use Task.Run instead of the Task constructor and Start; and use await rather than ContinueWith:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
await task;
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
Task.Run and await work more naturally with async code.
You shouldn't be using the Task constructor directly to start tasks, especially when starting async tasks. If you want to offload work to be executed in the background use Task.Run instead:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
About the continuation, it would be better to just append it's logic to the end of the lambda expression. But if you're adamant on using ContinueWith you need to use Unwrap to get the actual async task and store the it so you could handle exceptions:
task = task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
}).Unwrap();
try
{
await task;
}
catch
{
// handle exceptions
}
Change your code to
// Setup task and continuation
var t1 = new Task(() =>
{
Console.WriteLine("TASK Starting");
Task.Delay(1000).Wait(); // actual work here
Console.WriteLine("TASK Finishing");
});
var t2 = t1.ContinueWith((x) =>
{
Console.WriteLine("C1");
Task.Delay(100).Wait(); // actual work here
Console.WriteLine("C2");
});
t1.Start();
// Exception will be swallow without the line below
await Task.WhenAll(t1, t2);
I was just experimenting to see what happens when a cold task (i.e. a Task which hasn't been started) is awaited. To my surprise the code just hung forever and "Finsihed" is never printed. I would expect that an exception is thrown.
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
}
void Main()
{
Test1().Wait();
Console.WriteLine("Finished");
}
Then I though perhaps the task can be started from another thread, so I changed the code to:
public async Task Test1()
{
var task = new Task(() => Thread.Sleep(1000));
//task.Start();
await task;
Console.WriteLine("Test1 Finished");
}
void Main()
{
var task1 = Test1();
Task.Run(() =>
{
Task.Delay(5000);
task1.Start();
});
task1.Wait();
Console.WriteLine("Finished");
}
But it is still blocked at task1.Wait(). Does anyone know if there is way to start a cold task after it has being awaited?
Otherwise it seems there is no point in being able to await a cold task, so perhaps the task should either be started when awaited or an exception should be thrown.
Update
I was awaiting the wrong task, i.e. the outer task returned by Test1 rather than the one newed inside it. The InvalidOperationException mentioned by #Jon Skeet was being thrown inside Task.Run however because the resulting task was not observed, the exception was not thrown on the main thread. Putting a try/catch inside Task.Run or calling Wait() or Result on the task returned by Task.Run threw the exception on the main console thread.
You're trying to start the task returned by the async method - that isn't the cold task that you started out with. Indeed, if you add some diagnostics to your Task.Run call, you'll see that an exception is thrown:
System.InvalidOperationException: Start may not be called on a promise-style task.
Here's an example showing what I think you were really trying to do:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Test
{
static void Main(string[] args)
{
// Not using Task.Delay! That would be pointless
Task t1 = new Task(() => Thread.Sleep(1000));
Task t2 = Await(t1);
Console.WriteLine(t2.Status);
Console.WriteLine("Starting original task");
t1.Start();
Console.WriteLine(t2.Status);
t2.Wait();
Console.WriteLine(t2.Status);
}
static async Task Await(Task task)
{
Console.WriteLine("Beginning awaiting");
await task;
Console.WriteLine("Finished awaiting");
}
}
Note the use of Thread.Sleep instead of Task.Delay; unless you're using the result of Task.Delay, it basically does nothing. Using Thread.Sleep is emulating real work to be done in the other task.
As for why awaiting an unstarted task doesn't throw an exception - I think that's reasonable, to be honest. It allows for situations like the above to be valid, which may in some cases make life easier. (You may create a lot of tasks before starting them, for example - and you may want to start waiting for them to finish before you start them.)
Does anyone know if there is way to start a cold task after it has
being awaited?
You still can create a cold task from an async method and start it later, if that's what you want:
class Program
{
public static async Task Test1()
{
await Task.Delay(1000);
Console.WriteLine("Test1 is about to finish");
}
static void Main(string[] args)
{
var taskOuter = new Task<Task>(Test1);
var taskInner = taskOuter.Unwrap();
Task.Run(() =>
{
Thread.Sleep(2000);
// run synchronously
taskOuter.RunSynchronously();
// or schedule
// taskOuter.Start(TaskScheduler.Defaut);
});
taskInner.Wait();
Console.WriteLine("Enter to exit");
Console.ReadLine();
}
}