Async methods wait for each other - c#

I am trying to run async program.
There is main:
using System;
using System.Threading.Tasks;
namespace Test
{
class Program
{
static void Main(string[] args)
{
RunTestsAsync().Wait();
}
private async static Task RunTestsAsync()
{
var a = new SlowString("ab", true);
var b = new SlowString("c", true);
var result1 = a.Equal(b);
var result2 = b.Last(b);
var result3 = b.GreaterThen(a);
result1.Wait();
result2.Wait();
result3.Wait();
Console.WriteLine();
Console.WriteLine("Results: {0}, {1}, {2}", result1.Result, result2.Result, result3.Result);
Console.WriteLine();
}
}
}
And here is second file:
using System;
using System.Threading.Tasks;
namespace Test
{
class SlowString
{
private readonly string str;
private readonly bool msg;
private readonly int delay;
public SlowString(string str, bool msg = false, int delay = 30)
{
this.str = str;
this.msg = msg;
this.delay = delay;
}
public async Task<bool> Equal(SlowString other)
{
if(msg) Console.WriteLine("SlowString Equals Started");
Task.Delay(delay).Wait();
bool result = await Task.Run(() => { return str.Equals(other.str); });
if (msg) Console.WriteLine("SlowString Equals Ended");
return result;
}
public async Task<bool> GreaterThen(SlowString other)
{
if (msg) Console.WriteLine("SlowString GreaterThen Started");
Task.Delay(delay).Wait();
bool result = await Task.Run(() => { return str.CompareTo(other.str) > 0 ? true : false; });
if (msg) Console.WriteLine("SlowString GreaterThen Ended");
return result;
}
public async Task<SlowString> Last(SlowString other)
{
if (msg) Console.WriteLine("SlowString Last Started");
Task.Delay(delay).Wait();
SlowString result = await Task.Run(() => { return str.CompareTo(other.str) > 0 ? this : other; });
if (msg) Console.WriteLine("SlowString Last Ended");
return result;
}
public override string ToString()
{
return str;
}
}
}
Problem is my program always waits for previous computation to be completed, so I get:
SlowString Equals Started
SlowString Equals Ended
SlowString Last Started
SlowString Last Ended
SlowString GreaterThen Started
SlowString GreaterThen Ended
Even with greater delay, like 3000ms program stil waits until result1 is computed, then goes to result2 and computes it, and only then goes further.
I think I've really tried everything. Please give me some clue.

You are calling .Wait(), which, as the name would suggests, waits until the task is completed. It blocks the execution. So first thing would be never call .Wait() or .Result or any of the like. Always use await with your async methods.
But simply changing .Wait() to await won't do, as you're trying to run the tasks in parallel. In that case your routine should look more like:
var result1 = Task.Run(() => a.Equal(b));
var result2 = Task.Run(() => b.Last(b));
var result3 = Task.Run(() => b.GreaterThen(a));
await Task.WhenAll(new [] {result1, result2, result3});
On other notes:
Task.Delay(delay).Wait();
should also be
await Task.Delay(delay);
And lines like this:
bool result = await Task.Run(() => { return str.CompareTo(other.str) > 0 ? true : false; });
don't do anything other than apply an overhead: you're calling a new task on the thread pool and immediately awaiting the result. It's synchronous wrapped in an artificial asynchronicity. This:
bool result = str.CompareTo(other.str) > 0 ? true : false;
would probably run quicker, because of the lack of an overhead.
And your Main method should be:
static async Task Main(string[] args)
{
await RunTestsAsync();
}

Related

How to track Task's status with extension method?

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

What is the best approach/practice for awaiting multiple independent tasks?

var contractSchemaTask = contractSchemaRepository.GetByContractIdAsync(data.Id);
var sectionsTask = sectionRepository.GetAllByContractIdAsync(id);
var latestContractIdTask = contractRepository
.GetLatestContractIdByFolderIdAsync(data.FolderId.Value);
List<Task> allTasks = new List<Task>()
{ contractSchemaTask, sectionsTask, latestContractIdTask };
while (allTasks.Any())
{
Task finished = await Task.WhenAny(allTasks);
if (finished == contractSchemaTask)
{
var contractSchema = await contractSchemaTask;
result.ReturnData.IsSchedules = contractSchema.Count > 0 ? true : false;
}
else if (finished == sectionsTask)
{
List<Section> sections = await sectionsTask;
List<TextValueVM> SectionTabList = sections.Count > 0 ? sections
.OrderBy(a => a.SectionNumber)
.Select(a => new TextValueVM()
{ Text = a.ToString(), Value = a.Id.ToString() })
.ToList() : new List<TextValueVM>();
bool IsSectionsLinked = false;
int linkSectionCount = sections
.Where(x => x.LinkSectionId != null && x.LinkSectionId != Guid.Empty)
.ToList()
.Count();
if (linkSectionCount == 0 && sections.Count > 0)
{
List<Guid> sectionIds = sections.Select(x => x.Id.Value).ToList();
List<Section> currentContractLinkSections = await sectionRepository
.GetSectionsByLinkSectionIdAsync(sectionIds);
if (currentContractLinkSections.Count > 0)
{
IsSectionsLinked = true;
}
}
else if (linkSectionCount > 0)
{
IsSectionsLinked = true;
}
result.ReturnData.SectionTabList = SectionTabList;
result.ReturnData.IsSectionsLinked = IsSectionsLinked;
}
else if (finished == latestContractIdTask)
{
Guid LatestContractId = await latestContractIdTask;
result.ReturnData.isLatestContract
= (data.Id == LatestContractId) ? true : false;
}
allTasks.Remove(finished);
}
I am working on a asp.net core 3.0 WebAPI project. Above is the sample code for independent tasks that I handle using a while loop. Is there any better or efficient approach for handling independent tasks in the asynchronous programming?
P.S: All the 3 tasks are independent and may vary on their response time depending upon the number of records fetched from the database.
You should do it like this:
public Task Main()
{
var result = new Result();
return Task.WhenAll(TaskOne(result), TaskTwo(result), TaskThree(result));
}
private async Task TaskOne(Result result)
{
var contractSchema = await contractSchemaRepository.GetByContractIdAsync(data.Id);
//your logic for task1, set related result properties
}
private async Task TaskTwo(Result result)
{
var sections = await sectionRepository.GetAllByContractIdAsync(id);
//your logic for task2, set related result properties
}
private async Task TaskThree(Result result)
{
var latestContractId = await contractRepository.GetLatestContractIdByFolderIdAsync(data.FolderId.Value);
//your logic for Task3, set related result properties
}
Result class should be implemented as thread-safe because tasks can be executed simultaneously. If you just set different properties in each method it should be OK.
Combining Task.WhenAll with Continue allows you to execute code as soon the task finish without having to await the rest of the tasks.
class Test {
public static async Task Main() {
var t1 = AsyncWork1().ContinueWith((t) => Console.WriteLine($"Task1 finished with value {t.Result}"));
var t2 = AsyncWork2().ContinueWith((t) => Console.WriteLine($"Task2 finished with value {t.Result}"));
var t3 = AsyncWork3().ContinueWith((t) => Console.WriteLine($"Task3 finished with value {t.Result}"));
await Task.WhenAll(new[] { t1, t2, t3 });
//here we know that all tasks has been finished and its result behaviour executed.
Console.ReadKey();
}//main
public static async Task<int> AsyncWork1() {
await Task.Delay(1000);
return 1;
}
public static async Task<string> AsyncWork2() {
await Task.Delay(100);
return "work2";
}
public static async Task<bool> AsyncWork3() {
await Task.Delay(500);
return true;
}
}//class Test
Compare with this:
class Test {
public static async Task Main() {
var t1 = AsyncWork1();
var t2 = AsyncWork2();
var t3 = AsyncWork3();
await Task.WhenAll(new[] { t1, t2, t3 });
//all task finished but now we have to execute the result behaviour in a sync way
Console.WriteLine($"Task1 finished with value {t1.Result}");
Console.WriteLine($"Task2 finished with value {t2.Result}");
Console.WriteLine($"Task3 finished with value {t3.Result}");
Console.ReadKey();
}//main
public static async Task<int> AsyncWork1() {
await Task.Delay(1000);
return 1;
}
public static async Task<string> AsyncWork2() {
await Task.Delay(100);
return "work2";
}
public static async Task<bool> AsyncWork3() {
await Task.Delay(500);
return true;
}
}//class Test

Async And Await in C# And problems

This program does not print the output in the correct order.
public static void Main(string[] args)
{
new Program().Start();
}
public async void Start()
{
int num1 = await GetNumber();
int num2 = await GetNumber();
int num3 = await GetNumber();
Console.WriteLine("Wait...");
Console.ReadKey();
}
public static async Task<int> GetNumber()
{
System.Threading.Thread.Sleep(4000);
Console.WriteLine("Hello");
return 0;
}
It outputs:
--------wait 4Seconds
--------print Hello
--------wait 4Seconds
--------print Hello
--------wait 4Seconds
--------print Hello
--------print wait....
It should output
--------print wait....
--------wait 4Seconds
--------print Hello
--------print Hello
--------print Hello
Use
Await Task.Delay(Timespan.FromMilliSeconds (4000))
instead of Thread.Sleep.
The fully worked out example.
using System;
using System.Threading.Tasks;
namespace Brad
{
public class Program
{
public static void Main(string[] args)
{
var task = new Program().Start();
Console.WriteLine("Wait...");
// You have to put a synchronous Wait() here because
// Main cannot be declared as async
task.Wait();
}
public async Task Start()
{
int num1 = await GetNumber();
int num2 = await GetNumber();
int num3 = await GetNumber();
Console.WriteLine("Finished");
}
public static async Task<int> GetNumber()
{
await Task.Delay(TimeSpan.FromMilliseconds(400));
Console.WriteLine("Hello");
return 0;
}
}
}
You can see it running here
https://dotnetfiddle.net/KHJaDZ
or maybe you wanted the tasks running in parallel instead of one after the other. You can try
using System;
using System.Threading.Tasks;
namespace Brad
{
public class Program
{
public static void Main(string[] args)
{
var task = new Program().Start();
Console.WriteLine("Wait...");
// You have to put a synchronous Wait() here because
// Main cannot be declared as async
task.Wait();
}
public async Task Start()
{
var task1 = GetNumber();
var task2 = GetNumber();
var task3 = GetNumber();
// This runs the tasks in parallel
await Task.WhenAll(task1, task2, task3);
Console.WriteLine("Finished");
}
public static async Task<int> GetNumber()
{
await Task.Delay(TimeSpan.FromMilliseconds(400));
Console.WriteLine("Hello");
return 0;
}
}
}
and this is running here.
https://dotnetfiddle.net/kVk77Z
await means "break the method in half here and come back later when this call finishes". It's how you "convert" a Task<T> into a T: by waiting for (awaiting) the task. Otherwise you are stuck with a Task<T>.
It seems like what you are looking for instead is not awaiting the tasks so that they run asynchronously, but if you did that then you wouldn't be able to get the int results.
(As the other answer mentions, you also need to await something in GetNumber or it will not actually be asynchronous.)
Something like:
public static void Main(string[] args) {
new Program().Start();
}
public void Start() {
GetNumber();
GetNumber();
GetNumber();
Console.WriteLine("Wait...");
Console.ReadKey();
}
public static async Task<int> GetNumber() {
await Task.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine("Hello");
return 0;
}
should give the output you expect:
Wait...
Hello
Hello
Hello

async/await not working in console application

I'm totally green with TPL and want to execute an async method in a console application.
My code:
static void Main()
{
Task<string> t = MainAsync();
t.Wait();
Console.ReadLine();
}
static async Task<string> MainAsync()
{
var result = await (new Task<string>(() => { return "Test"; }));
return result;
}
This task runs forever. Why? What am I missing?
You don't start your task. This is why Wait doesn't return. Try
var result = await Task.Run<string>(() => { return "Test"; });

How to Unit Test a void method with a Task inside

I have a graphic method CancelChanges() used and called by a ViewModel.
I want to test this method but we have a Task inside.
We use a Task to not freeze the UI.
My test method needs to wait the result of this Task to check the result.
The code is:
public override void CancelChanges()
{
Task.Run(
async () =>
{
this.SelectedWorkflow = null;
AsyncResult<IncidentTypeModel> asyncResult = await this.Dataprovider.GetIncidentTypeByIdAsync(this.Incident.Id);
Utils.GetDispatcher().Invoke(
() =>
{
if (asyncResult.IsError)
{
WorkflowMessageBox.ShowException(
MessageHelper.ManageException(asyncResult.Exception));
}
else
{
this.Incident = asyncResult.Result;
this.Refreshdesigner();
this.HaveChanges = false;
}
});
});
}
And my test method:
/// <summary>
/// A test for CancelChanges
/// </summary>
[TestMethod]
[TestCategory("ConfigTool")]
public void CancelChangesTest()
{
string storexaml = this._target.Incident.WorkflowXamlString;
this._target.Incident.WorkflowXamlString = "dsdfsdgfdsgdfgfd";
this._target.CancelChanges();
Assert.IsTrue(storexaml == this._target.Incident.WorkflowXamlString);
Assert.IsFalse(this._target.HaveChanges);
}
How can we do to have my test that is waiting the result of the Task?
Thanks.
Make the CancelChanges method return a Task, and then await this or set up a continuation in the test method. Some this like
public override Task CancelChanges()
{
return Task.Factory.StartNew(() =>
{
// Do stuff...
});
}
notice the change from Task.Run to Task.Factory.StartNew. This is a better way of starting tasks in such cases. Then in the test method
[TestMethod]
[TestCategory("ConfigTool")]
public void CancelChangesTest()
{
string storexaml = this._target.Incident.WorkflowXamlString;
this._target.Incident.WorkflowXamlString = "dsdfsdgfdsgdfgfd";
this._target.CancelChanges().ContinueWith(ant =>
{
Assert.IsTrue(storexaml == this._target.Incident.WorkflowXamlString);
Assert.IsFalse(this._target.HaveChanges);
});
}
You could also mark the test method as async and use await in the test method to do the same thing.
I hope this helps.
I would take the refactoring one step further, and if possible avoid using Task.Run. Since all you do is await and then invoke work on the UI Thread, I would do the following:
public override Task CancelChanges()
{
this.SelectedWorkflow = null;
AsyncResult<IncidentTypeModel> asyncResult = await this.Dataprovider.GetIncidentTypeByIdAsync(this.Incident.Id);
if (asyncResult.IsError)
{ WorkflowMessageBox.ShowException(MessageHelper.ManageException(asyncResult.Exception));
}
else
{
this.Incident = asyncResult.Result;
this.Refreshdesigner();
this.HaveChanges = false;
}
});
});
}
And the test method:
[TestMethod]
[TestCategory("ConfigTool")]
public async Task CancelChangesTest()
{
string storexaml = this._target.Incident.WorkflowXamlString;
this._target.Incident.WorkflowXamlString = "dsdfsdgfdsgdfgfd";
var cancelChanges = await this._target.CancelChanges();
Assert.IsTrue(storexaml == this._target.Incident.WorkflowXamlString);
Assert.IsFalse(this._target.HaveChanges);
}

Categories