Task indicates IsCompleted even though it is just awaiting - c#

I have the following example code. The problem is, that the Task indicates IsCompleted even though it just await.
longTask only awaits (Task.Delay()) and is not yet finished, so why is IsCompleted true? longTask can't every be completed, because it is caught in while(true)?!
static async Task Main(string[] args)
{
Task longTask = Task.Factory.StartNew(async () =>
{
while (true)
{
long count = 0;
Console.WriteLine("doing hard work.");
while (count < 99999999)
{
count++;
}
Console.WriteLine("wait for a moment");
await Task.Delay(1000);
Console.WriteLine("I have waited enough");
}
});
while (true)
{
Console.WriteLine($"status: {longTask.IsCompleted}");
await Task.Delay(100);
}
}

If you want longTask to represent the inner, never ending, task then call .Unwrap() with appropriate settings or use Task.Run which has the sensible defaults:
public static class Program
{
static async Task Main(string[] args)
{
Task longTask = Task.Factory.StartNew(async () =>
{
while (true)
{
long count = 0;
Console.WriteLine("doing hard work.");
while (count < 99999999)
{
count++;
}
Console.WriteLine("wait for a moment");
await Task.Delay(1000);
Console.WriteLine("I have waited enough");
}
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap();
while (true)
{
Console.WriteLine($"status: {longTask.IsCompleted}");
await Task.Delay(100);
}
}
}
or
public static class Program
{
static async Task Main(string[] args)
{
Task longTask = Task.Run(async () =>
{
while (true)
{
long count = 0;
Console.WriteLine("doing hard work.");
while (count < 99999999)
{
count++;
}
Console.WriteLine("wait for a moment");
await Task.Delay(1000);
Console.WriteLine("I have waited enough");
}
});
while (true)
{
Console.WriteLine($"status: {longTask.IsCompleted}");
await Task.Delay(100);
}
}
}
For more info: MSDN & Startnew is Dangerous

Related

Continuation code (ContinueWith) not getting executed

I have the following code where the continuation code isn't getting executed.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Konsole
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main starting...");
Task.Run(async () =>
{
await Job1();
})
.ContinueWith(async t =>
{
await Job2();
}).Wait();
Console.WriteLine("Main ending...");
}
public static async Task Job1()
{
await Task.Run(() =>
{
for (int i = 1; i <= 2; i++)
{
Thread.Sleep(1000);
Console.WriteLine($"Job1 {i}");
}
});
}
public static async Task Job2()
{
await Task.Run(() =>
{
for (int i = 1; i <= 2; i++)
{
Thread.Sleep(1000);
Console.WriteLine($"Job2 {i}");
}
});
}
}
}
Console output:
Main starting...
Job1 1
Job1 2
Main ending...
I expected Job2 to be executed as well before the application closed.
To answer you question, you need to 'Unwrap' the task from the first async call as follows:
Task.Run(async () =>
{
await Job1();
})
.ContinueWith(async t =>
{
await Job2();
})
.Unwrap()
.Wait();
Unwrap Documentation
There are a number of other things to comment on here though.
If you are using async/await, you should really use it all the way through. Your question doesn't specify which version of c# you are using. If you are using C# 7.1 or above, you should make your main method an async method as follows:
public static async Task Main()
{
Console.WriteLine("Main starting...");
await Job1();
await Job2();
Console.WriteLine("Main ending...");
}
Also, as pointed out by #Sean, Thread.Sleep is a bad idea in these scenarios, you should use await Task.Delay(1000); instead.
Task.Delay Documentation
A great article with some async/await best practices can be found on MSDN as well.
You should not use ContinueWith; it's a dangerous, low-level method. In this case, you're encountering a problem because ContinueWith does not understand asynchronous delegates.
Ideally, you should replace all calls to ContinueWith (and Wait) with await:
static async Task Main(string[] args)
{
Console.WriteLine("Main starting...");
await Task.Run(async () =>
{
await Job1();
});
await Job2();
Console.WriteLine("Main ending...");
}
If you break out the variables so you can see the types, it will become more clear why the original code wasn't working:
static async Task Main(string[] args)
{
Console.WriteLine("Main starting...");
Task runTask = Task.Run(async () =>
{
await Job1();
});
Task<Task> continuationTask = runTask.ContinueWith(async t =>
{
await Job2();
});
// continuationTask is a Task<Task>.
// This waits for the "outer" task only, not the "inner" task.
continuationTask.Wait();
Console.WriteLine("Main ending...");
}

Check if CancellationToken has been cancelled

I've created a little demo project to help me understand how I can use Cancellation Tokens. I understand that you cancel a token and check if a cancellation has been requested, but is there a way that I can check whether the cancellation has been realised? In my example below, I don't want to run Work() again until DoWork() has finished running.
public class Program
{
public static CancellationTokenSource tokenSource;
private static void Main(string[] args)
{
while (true)
{
Work();
}
}
public static async void Work()
{
tokenSource = new CancellationTokenSource();
Console.WriteLine("Press any key to START doing work");
Console.ReadLine();
Console.WriteLine("Press any key to STOP doing work");
DoWork(tokenSource.Token);
Console.ReadLine();
Console.WriteLine("Stopping...");
tokenSource.Cancel();
}
public static async void DoWork(CancellationToken cancelToken)
{
while (true)
{
Console.WriteLine("Working...");
await Task.Run(() =>
{
Thread.Sleep(1500);
});
if (cancelToken.IsCancellationRequested)
{
Console.WriteLine("Work Cancelled!");
return;
}
}
}
}
You typically don't want to make your DoWork function async void -- make it async Task instead. That way you can see when it's completed (or been cancelled).
You also probably want to use cancelToken.ThrowIfCancellationRequested(). This throws an OperationCanceledException, which you can catch.
public class Program
{
public static CancellationTokenSource tokenSource;
private static async Task Main(string[] args)
{
while (true)
{
await Work();
}
}
public static async Task Work()
{
tokenSource = new CancellationTokenSource();
Console.WriteLine("Press any key to START doing work");
Console.ReadLine();
Console.WriteLine("Press any key to STOP doing work");
var task = DoWork(tokenSource.Token);
Console.ReadLine();
Console.WriteLine("Stopping...");
tokenSource.Cancel();
try
{
await task;
}
catch (OperationCanceledException)
{
// Task was cancelled
}
}
public static async Task DoWork(CancellationToken cancelToken)
{
while (true)
{
Console.WriteLine("Working...");
await Task.Run(() =>
{
Thread.Sleep(1500);
});
cancelToken.ThrowIfCancellationRequested();
}
}
}
This code relies on "async main", which was introduced in C# 7. If you don't have this, you can write your Main method as:
private static void Main(string[] args)
{
while (true)
{
Work().Wait();
}
}
You will make the most out of a CancellationToken if all your operations are asynchronous and cancellable. This way cancelling the token will have immediate effect. You won't have to wait for a Thread.Sleep or other blocking call to complete.
public static async Task DoWork(CancellationToken cancellationToken)
{
while (true)
{
await Console.Out.WriteLineAsync("Working...");
await Task.Delay(1500, cancellationToken);
}
}
In this example the token is passed only to Task.Delay, because the WriteLineAsync is not cancellable on .NET Framework (it is on .NET Core).
An OperationCanceledException will be raised by Task.Delay when the token is cancelled.

How to cancel a task and throw an exception without calling IsCancellationRequested or ThrowIfCancellationRequested

I create 2 tasks. Task1 runs a long calculations (SingleLongMethod), Task2 has to cancel Task1 in the middle of the calculations.
The solution I found is to call
token.ThrowIfCancellationRequested() or token.IsCancellationRequested.
The question is: can I throw an Exception in the Task1, automatically after calling token.Cancel() in Task2 without repeatedly calling token.ThrowIfCancellationRequested()?
static void Main(string[] args)
{
Task.Factory.StartNew(() =>
{
token.Register(() =>
{
//throw new Exception("Help!");
});
SingleLongMethod();
});
Task.Factory.StartNew(() =>
{
StopMethod();
});
Console.ReadKey();
}
private static void SingleLongMethod()
{
try
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine("Test pcb");
Thread.Sleep(1000);
//token.ThrowIfCancellationRequested();
}
}
catch (Exception exc)
{
Console.WriteLine("I was stopped!");
}
finally
{
Console.WriteLine("End!");
}
}
private static void StopMethod()
{
try
{
for (int i = 0; i < 30; i++)
{
if (i == 5)
tokenSource.Cancel();
Console.WriteLine("Monitoring state");
Thread.Sleep(1000);
}
}
catch (Exception)
{
throw new Exception("Help!");
}
}
I tried to throw and exception in registering callbacks but it didn't work out.
Forcing a running thread to exit is a bad practice and not supported in .NET because the threads are managed and controlled by the runtime that does not even allow you to do this.
There are 2 ways to end the execution of a task:
The way that you are currently using, which is already good enough.
Instead of using a CancellationToken, you could await a task that is cancelled.
Example:
static async Task Main(string[] args)
{
var cancelTask = Task.CompletedTask;
var task1 = Task.Run(async () =>
{
try
{
for (var i = 0; i < 20; i++)
{
Console.WriteLine("Test pcb");
await Task.Delay(1000);
await cancelTask;
}
}
catch
{
Console.WriteLine("I was stopped!");
}
finally
{
Console.WriteLine("End!");
}
});
var task2 = Task.Run(async () =>
{
try
{
for (var i = 0; i < 30; i++)
{
if (i == 5)
{
cancelTask = Task.FromException(new OperationCanceledException());
}
Console.WriteLine("Monitoring state");
await Task.Delay(1000);
}
}
catch
{
throw new Exception("Help!");
}
});
await Task.WhenAll(task1, task2);
}

Proper async looping in console app

I know async I/O doesn't bring parallelism but I thought when the app is awaiting an async operation it could carry on doing other stuff.
Consider the below code I would expect the loop to carry on while awaiting Wait(i) but obviously I was wrong and each iteration is blocking. What's the correct way to achieve some concurrency here?
using System;
using System.Threading.Tasks;
namespace asynctest
{
class Program
{
static void Main(string[] args)
{
Do().GetAwaiter().GetResult();
}
public static async Task Do()
{
for(int i=0;i<10;i++)
{
await Wait(i);
}
}
public static async Task Wait(int i)
{
await Task.Delay(10000);
}
}
}
public static async Task Do()
{
var tasks = new Task[10];
for (int i = 0; i < 10; i++)
{
tasks[i] = Wait(i);
}
await Task.WhenAll(tasks);
}
You should use WhenAll() to combine the task results, thus your tasks will run in parallel:
namespace asynctest
{
class Program
{
static void Main(string[] args)
{
Do().GetAwaiter().GetResult();
}
public static async Task Do()
{
for(int i=0;i<10;i++)
{
var task1 = Wait(5000);
var task2 = Wait(3000);
int[] result = await Task.WhenAll(task1, task2);
Console.WriteLine("waited for a total of " + result.Sum() + " ms");
}
}
public static async Task<int> Wait(int i)
{
await Task.Delay(i);
return i;
}
}
}

Why isn't Task.WhenAll working?

Here's some code I'm working with, that fires of three tasks. My Stop is called, and from the output, it looks as though the tasks finish after Stop returns, which isn't what I'd expect.
This is Visual Studio 2013 with .NET 4.5.1
I tried the same using AutoResetEvent which worked as expected.
Here's the output from this:
Three tasks without auto reset
Starting...
FIRST
SECOND
THIRD
Running...
FIRST
FIRST
FIRST
SECOND
FIRST
FIRST
THIRD
...
SECOND
FIRST
Stopping...
THIRD
All done!
First done
Second done
Third done
What I'd expect to see is:
First done
Second done
Third done
All done!
Here's the code (I even added locking around the Console output, but it made no difference):
public class ThreeTasksWithoutAutoResetEvent
{
private CancellationTokenSource cancellation;
private Task[] tasks;
private object obj = new object();
public ThreeTasksWithoutAutoResetEvent()
{
Console.WriteLine("Three tasks without auto reset");
cancellation = new CancellationTokenSource();
tasks = new Task[3];
Message("Starting...");
Start();
Message("Running...");
Thread.Sleep(3000);
Message("Stopping...");
Stop();
Message("All done!");
}
private void Start()
{
tasks[0] = this.First(cancellation.Token);
tasks[1] = this.Second(cancellation.Token);
tasks[2] = this.Third(cancellation.Token);
}
private async void Stop()
{
cancellation.Cancel();
await Task.WhenAll(tasks);
}
private async Task First(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("FIRST");
Thread.Sleep(100);
}
Message("First done");
}, token);
}
private async Task Second(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("SECOND");
Thread.Sleep(300);
}
Message("Second done");
}, token);
}
private async Task Third(CancellationToken token)
{
await Task.Run(
() =>
{
while (!token.IsCancellationRequested)
{
Message("THIRD");
Thread.Sleep(500);
}
Message("Third done");
}, token);
}
private void Message(string message)
{
lock (obj)
{
Console.WriteLine(message);
}
}
}
Because you're not waiting for Stop method to finish. You can't wait for an async void method. You need return a Task, so that the caller can wait/await for it to complete.
private Task Stop()
{
cancellation.Cancel();
return Task.WhenAll(tasks);
}
Then you can call Stop().Wait(); instead of Stop. That will wait for WhenAll to complete.

Categories