How to track Task's status with extension method? - c#

I am new to asynchronous programming. Trying to create correct extension method which can print status of task if it has changed. But i dont know how to do it. Thats what i have now:
static class MyAsync
{
static void Main()
{
Task t = MyAsync.PrintCountPrimesAsync(35);
t.Tracking();
Thread.Sleep(1000);
}
public static async Task PrintCountPrimesAsync(int n) =>
Console.WriteLine($"CountOfPrimes = { await CustomMath.GetPrimesCountAsync(100000, 100000)}");
public static async Task Tracking(this Task task)
{
await Task.Run(() =>
{
TaskStatus current = task.Status;
while (!task.IsCompleted)
{
if (current != task.Status)
{
Console.WriteLine(task.Status);
current = task.Status;
}
}
});
}
}
class CustomMath
{
public static Task<int> GetPrimesCountAsync(int start, int count)
{
return Task.Run(() =>
ParallelEnumerable.Range(start, count).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
}
}

The ideal answer to this is "don't", but if you absolutely must, ContinueWith acts as a callback that might be suitable here:
public static void Tracking(this Task task)
=> _ = task.ContinueWith(static x => Console.WriteLine(x.Status));
This only tracks completion (with or without fault), but: that's pretty much the only interesting and reliable state transition anyway.

I linked the SO post that says it's unpractical to do that.
So, if you really need this, this is something you could use:
static void Main(string[] args)
{
var t = GetPrimes();
while (t.Status != TaskStatus.RanToCompletion) ;
}
But if do it like this specifically, it's going to do the same job as await opreator would do, but this blocks the thread.
So basically you would need to have some loop watching over tasks' statuses. You could add some Task.Delays no to block the thread.
But still, I would not recommend going that path.

I'd try to tackle this with a callback.
Try this:
class CustomMath
{
public static Task<int> GetPrimesCountAsync(int start, int count, Action<TaskStatus> track)
{
Task<int> task = null;
task = Task.Run(() =>
ParallelEnumerable.Range(start, count).Count(n =>
{
track(task.Status);
return Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0);
}));
return task;
}
}
I'm using the Action<TaskStatus> track and injecting it in to the calculation. It then allows the calculation to control when to report back the status.
Now you can run this code:
async Task Main()
{
var x = await CustomMath.GetPrimesCountAsync(1, 5, ts => Console.WriteLine(ts.ToString()));
Console.WriteLine(x);
}
The output I get is:
Running
Running
Running
Running
Running
4

Related

Application hangs while running async task

I am learning about async/await and created a dummy console application. When I try to get the result from async method the program just hangs. Any idea what is wrong in the following code.
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() => 5);
var x = TestAsync();
//x.Start();
Console.WriteLine(x.Result);
}
private static Task<int> CalculateValue()
{
Console.WriteLine("In CalculateValue"); // This line is printed.
Task<int> t = new Task<int>(GetValue); // The program hangs here.
return t;
}
public static async Task<int> TestAsync()
{
int result = await CalculateValue();
return result;
}
private static int GetValue()
{
return 10;
}
First of all:
Task<int> t = new Task<int>(GetValue); // The program hangs here.
is incorrect, the program actually hangs here :
Console.WriteLine(x.Result);
.Result blocks current thread until task x completes execution and returns result. It never completes as it awaits task returned by CalculateValue method whis is this task:
Task<int> t = new Task<int>(GetValue);
This is so called 'cold Task' which means that it's a task in an inactive state.
To start a 'hot' task (which basically means start the task) use the Task.Run method:
Task<int> t = Task.Run(GetValue);

Running parallel async tasks?

So I've been searching StackOverflow/Google for different methods of running multiple async tasks concurrently. There seemed to be quite the debate between different methods and I just wanted to get some clarification. I'm writing a program to execute a JSON POST request until the server returns a status code of 200. Let's say I want to run 5 of theses tasks in parallel until one returns a status code of 200. Please try not to stray away from the topic, I have no control over the server! Here's my current code,
static bool status = false;
public static async Task getSessionAsync() {
while(!status) { ... }
}
public static async Task doMoreStuff() {
...
}
public static async Task RunAsync()
{
await getSessionAsync ();
await doMoreStuff();
}
public static void Main (string[] args)
{
Task.WhenAll(RunAsync()).GetAwaiter().GetResult();
}
Basically, I'm wondering if it's wrong for me to approach it like this,
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
This will not run in parallel:
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
You have to use Task.WhenAny()
public static async Task RunAsync()
{
var tasks = new List<Task>();
for(int i = 0; i < 5; i++) {
tasks.Add(getSessionAsync());
}
await Task.WhenAny(tasks);
await doMoreStuff();
}
If you do not need your current context (i.e. when you are writing a Library and not Frontend code), don't forget to use ConfigureAwait(false) after each await.
Assuming:
private Task<MySession> GetSessionAsync()
{
// ...
}
Option 1
Task.WhenAny
var session = await await Task.WhenAny(Enumerable.Range(0, 5).Select(_ => GetSessionAsync()));
Option 2
You could use the Rx LINQ method called Amb which will observe only the first Observable that returns something.
var session = await Enumerable.Range(0, 5).Select(_ => GetSessionAsync().ToObservable()).Amb().ToTask();

Task finishes before expected

I have this method:
private static async Task MyMethod();
And it is invocated this way:
public static void Main()
{
s_Finishing = false;
Task printTask = PrintStatistics();
MyMethod(serversSawa, serversSterling).Wait();
s_Finishing = true;
}
I expect that PrintStatistics will stop to run only after MyMethod is completed. But unfortunately it doesn`t. If I comment the line s_Finishing = true; The task runs forever - and allows to MyMethod to be completed
How can I solve the issue?
private static async Task PrintStatistics()
{
while (!s_Finishing)
{
long total = 0;
await Task.Delay(TimeSpan.FromSeconds(20));
foreach (var statistic in s_Statistics)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
total += statistic.Value;
}
foreach (var statistic in s_StatisticsRegion)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
}
ToolsTracer.Trace("TOTAL:{0}", total);
ToolsTracer.Trace("TIME:{0}", s_StopWatch.Elapsed);
}
}
private static async Task MyMethod()
{
Parallel.ForEach(
data,
new ParallelOptions { MaxDegreeOfParallelism = 20 }, async serverAndCluster =>
{
await someMethod() });
}
I believe your problem is here:
Parallel.ForEach(..., async ...);
You can't use async with ForEach. It's extremely rare to need to do both parallel (CPU-bound) and async (I/O-bound) together in the same method. If you just want concurrency (which I suspect), use Task.WhenAll instead of ForEach. If you really do need both CPU parallelism and async, then use TPL Dataflow.

Create a Task list, with tasks without executing

I have an async method
private async Task DoSomething(CancellationToken token)
a list of Tasks
private List<Task> workers = new List<Task>();
and I have to create N threads that runs that method
public void CreateThreads(int n)
{
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
for (int i = 0; i < n; i++)
{
workers.Add(DoSomething(token));
}
}
but the problem is that those have to run at a given time
public async Task StartAllWorkers()
{
if (workers.Count > 0)
{
try
{
while (workers.Count > 0)
{
Task finishedWorker = await Task.WhenAny(workers.ToArray());
workers.Remove(finishedWorker);
finishedWorker.Dispose();
}
if (workers.Count == 0)
{
tokenSource = null;
}
}
catch (OperationCanceledException)
{
throw;
}
}
}
but actually they run when i call the CreateThreads Method (before the StartAllWorkers).
I searched for keywords and problems like mine but couldn't find anything about stopping the task from running.
I've tried a lot of different aproaches but anything that could solve my problem entirely.
For example, moving the code from DoSomething into a workers.Add(new Task(async () => { }, token)); would run the StartAllWorkers(), but the threads will never actually start.
There is another method for calling the tokenSource.Cancel().
You can use TaskCompletionSource<T> as a one-time "signal" to asynchronous methods.
So you'd create it like this:
private TaskCompletionSource<object> _tcs;
public void CreateThreads(int n)
{
_tcs = new TaskCompletionSource<object>();
tokenSource = new CancellationTokenSource();
token = tokenSource.Token;
for (int i = 0; i < n; i++)
{
workers.Add(DoSomething(_tcs.Task, token));
}
}
Then when you're ready to start the tasks, just complete the "start" signal task:
public Task StartAllWorkers()
{
_tcs.TrySetCompleted(null);
return Task.WhenAll(workers);
}
(The StartAllWorkers method above has slightly different semantics than your original method: your original method would throw a cancellation exception as soon as the first task canceled; this one will wait until all the methods complete and then throw a cancellation exception)
Then your DoSomething just has to honor the "start signal":
private static async Task DoSomething(Task start, CancellationToken token)
{
await start;
... // rest of your code
}
What about this idea:
Instead of holding a list of tasks, hold a list of TaskReference items:
public class TaskReference
{
private readonly Func<Task> _func;
public TaskReference(Func<Task> func)
{
_func = func;
}
public async Task RunAsync()
{
await _func();
}
}
Adding to the list works like this:
taskList.Add(new TaskReference(() => DoSomething(myToken)));
And execution like this:
await Task.WhenAll(taskList.Select(o => o.RunAsync()));

await not blocking until Task finishes

I am trying to block RequestHandler.ParseAll() with await ConsumerTask;, but when i set a breakpoint there, i ALWAYS get the "Done..." output first... and then Parse2() fails with a NullReferenceException. (thats my guess: "the GC starts cleaning up because _handler got out of scope")
Anyway, I can't figure out why that happens.
class MainClass
{
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
/* fill mUrls here with values */
await Task.Run(() => _handler.ParseSpecific(mUrls));
Console.WriteLine("Done...");
}
}
static class Parser
{
public static async Task<IEnumerable<string>> QueryWebPage(string url) { /*Query the url*/ }
public static async Task Parse1(Query query)
{
Parallel.ForEach(/*Process data here*/);
}
public static async Task Parse2(Query query)
{
foreach(string line in query.WebPage)
/* Here i get a NullReference exception because query.WebPage == null */
}
}
sealed class RequestHandler
{
private BlockingCollection<Query> Queue;
private Task ConsumerTask = Task.Run(() => /* call consume() for each elem in the queue*/);
private async void Consume(Query obj)
{
await (obj.BoolField ? Parser.Parse1(obj) : Parser.Parse2(obj));
}
public async void ParseSpecific(string[] urls)
{
foreach(string v in urls)
Queue.Add(new Query(await QueryWebPage(v), BoolField: false));
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
private async Task ParseAll(bool onlySome)
{
ReInit();
Parallel.ForEach(mCollection, v => Queue.Add(new Query(url, BoolField:false)));
Queue.CompleteAdding();
await ConsumerTask;
/* Process stuff further */
}
}
struct Query
{
public readonly string[] WebPage;
public readonly bool BoolField;
public Query(uint e, IEnumerable<string> page, bool b) : this()
{
Webpage = page.ToArray();
BoolField = b;
}
}
CodesInChaos has spotted the problem in comments. It stems from having async methods returning void, which you should almost never do - it means you've got no way to track them.
Instead, if your async methods don't have any actual value to return, you should just make them return Task.
What's happening is that ParseSpecific is only running synchronously until the first await QueryWebPage(v) that doesn't complete immediately. It's then returning... so the task started here:
await Task.Run(() => _handler.ParseSpecific(mUrls));
... completes immediately, and "Done" gets printed.
Once you've made all your async methods return Task, you can await them. You also won't need Task.Run at all. So you'd have:
public async void DoWork()
{
RequestHandler _handler = new RequestHandler();
string[] mUrls;
await _handler.ParseSpecific(mUrls);
Console.WriteLine("Done...");
}
...
public async TaskParseSpecific(string[] urls)
{
foreach(string v in urls)
{
// Refactored for readability, although I'm not sure it really
// makes sense now that it's clearer! Are you sure this is what
// you want?
var page = await QueryWebPage(v);
Queue.Add(new Query(page, false);
}
Queue.CompleteAdding();
await ConsumerTask;
await ParseAll(true);
}
Your Reinit method also needs changing, as currently the ConsumerTask will basically complete almost immediately, as Consume will return immediately as it's another async method returning void.
To be honest, what you've got looks very complex, without a proper understanding of async/await. I would read up more on async/await and then probably start from scratch. I strongly suspect you can make this much, much simpler. You might also want to read up on TPL Dataflow which is designed to make producer/consumer scenarios simpler.

Categories