Dissimilar behavior of async-await - c#

I have stumbled upon a strange behavior of async-await.
Example code:
public class foo
{
public async static Task<myobj> method1()
{
var result = await method2();
return result;
}
private async static Task<myobj> method2()
{
// omitted for brevity.
}
}
public class bar
{
public void caller()
{
var result = foo.method1().Result;
pass(result);
}
}
This freezes the UI. The solution is to implement async-await on caller().
But how about this:
public class foo
{
public static myobj method1()
{
var result = method2().Result;
return result;
}
private async static Task<myobj> method2()
{
// omitted for brevity.
}
}
public class bar
{
public void caller()
{
var result = foo.method1();
pass(result);
}
}
This works freely.
What is different with private call vs. the one made to upstream method from other class?

As I mentioned in the comment, the first case is described in great details by Stephen Cleary in his blog.
The deadlock occurs at await method2(). The await continuation was posted to the UI thread's synchronization context via SynchronizationContext.Post. But the UI thread is already blocked waiting at this line: foo.method1().Result. The message pump is blocked and the continuation callback never gets pumped and invoked, deadlock.
In the second case, I don't see await anywhere. I.e., the code as you shown it doesn't do any asynchrony. I guess that's why it works.

Related

Is this usage of Task.Run() bad practice?

If the use of Task.Run in this case justifiable ?
Currently I run this code in a WinForms app, but later on it will be used in a ASP.NET project as a HostedService/BackgroundService. I am not sure if this is comparable then.
After reading multiple blogs about async/await and Tasks I feel like the Task.Run(() => .. should be implemented in the calling method Manager.SyncLoop(). But what if the implementation of IConnection is truely asynchronous, wouldn't that be code smell ?
private async void button1_Click(object sender, EventArgs e)
{
// this should be handled by the BackgroudService, WinForms is used just for testing
var m = new Manager();
m.Connection = new ConnectionA();
m.ExecuteAsync();
}
}
public interface IConnection
{
Task<object> ReadAsync();
}
// assume that i cannot change this
public class SomeLib
{
private Random random = new Random();
public object SyncReading()
{
Thread.Sleep(5000);
return random.Next(); ;
}
}
public class ConnectionA : IConnection
{
private SomeLib lib = new SomeLib();
public Task<object> ReadAsync()
{
// is this usage of Task.Run ok?
var v = Task.Run(() => lib.SyncReading());
return v;
}
// this will block UI
//public Task<object> ReadAsync()
//{
// return Task.FromResult(lib.SyncReading());
//}
}
public class Manager
{
public IConnection Connection { get; set; }
public async Task ExecuteAsync()
{
await SyncLoop();
}
public async Task SyncLoop()
{
while (true)
{
var i = await Connection.ReadAsync();
await Task.Delay(2000);
}
}
}
First, can you change IConnection? Is this synchronous implementation the primary one, or is it just one of many?
If you can change IConnection, then make it synchronous, and you can use Task.Run in the implementation of ExecuteAsync.
If IConnection needs to remain asynchronous, then I would say to implement ConnectionA.ReadAsync synchronously. Then have the Task.Run in ExecuteAsync as normal. The key behind this technique is that an asynchronous (Task-returning) signature means that the implementation may be asynchronous, not that it must be asynchronous.

Correct way of returning a task in C#

I have a scenario wherein I need to return a method that can be awaited from the caller.
I have here my implementations, and I just want to know which of which is the correct one.
Method 1.
public class ClassA
{
public Task MyTask { get; set; }
public ClassA()
{
MyTask = MyAsyncMethod();
}
private async void MyAsyncMethod()
{
await LongProcessHere();
}
}
public class MyCaller()
{
private async void ExecuteAsync()
{
ClassA ca = new ClassA();
await ca.MyTask;
}
}
Method 2.
public class ClassA
{
public Task MyAsyncMethod()
{
return Task.Run(async()=>
{
await LongProcessHere();
});
}
}
public class MyCaller()
{
private async void ExecuteAsync()
{
ClassA ca = new ClassA();
await ca.MyAsyncMethod();
}
}
If you're asking should I expose a property or method, then that entirely depends on what the Task represents.
If the task is something done once per instance of the class, then having a Task property is appropriate. Usually in this case, the property represents something about the instance, such as "my initialization is complete" or "I am done processing".
If the task is something that you need to do multiple times, then having a Task-returning method is appropriate.
Task-returning methods are vastly more common than Task properties.
On a side note, avoid async void and don't use Task.Run unnecessarily.
The correct code would look like:
public class ClassA
{
// always return a Task from an async method
public async Task MyAsyncMethod()
{
await LongProcessHere();
}
}
public class MyCaller
{
private async Task ExecuteAsync()
{
ClassA ca = new ClassA();
await ca.MyAsyncMethod();
}
}
You do not need to use Task.Run to call an async method. Also async methods should have Task return type as opposed to void. And vice versa.
public class ClassA
{
public async Task MyAsyncMethod()
{
return await LongProcessHere();
}
}
public class MyCaller()
{
private async Task ExecuteAsync()
{
ClassA ca = new ClassA();
await ca.MyAsyncMethod();
}
}

Waiting for all jobs to be finished with async await

I'm trying to understand the usage of async-await in C#5. If I have 2 jobs started in a method, is there a best way to wait for their completion in C#5+ ? I've done the example below but I fail to see what the async await keywork brings here besides free documentation with async keyword.
I made the following example, I want "FINISHED !" to be printed last. It is not the case however. What did I miss ? How can I make the async method wait until all jobs are finished ? Is there a point using async-await here ? I could just do Task.WaitAll with a non-async method here. I don't really understand what async brings in case you want to wait.
class Program
{
static void Main(string[] args)
{
var fooWorker = new FooWorker();
var barWorker = new BarWorker();
var test = new Class1(fooWorker, barWorker);
test.SomeWork();
Console.ReadLine();
}
}
public class Foo
{
public Foo(string bar) => Bar = bar;
public string Bar { get; }
}
public class Class1
{
private IEnumerable<Foo> _foos;
private readonly FooWorker _fooWorker;
private readonly BarWorker _barWorker;
public Class1(FooWorker fooWorker, BarWorker barWorker)
{
_fooWorker = fooWorker;
_barWorker = barWorker;
}
public void SomeWork()
{
_foos = ProduceManyFoo();
MoreWork();
Console.WriteLine("FINISHED !");
}
private async void MoreWork()
{
if (_foos == null || !_foos.Any()) return;
var fooList = _foos.ToList();
Task fooWorkingTask = _fooWorker.Work(fooList);
Task barWorkingTask = _barWorker.Work(fooList);
await Task.WhenAll(fooWorkingTask, barWorkingTask);
}
private IEnumerable<Foo> ProduceManyFoo()
{
int i = 0;
if (++i < 100) yield return new Foo(DateTime.Now.ToString(CultureInfo.InvariantCulture));
}
}
public abstract class AWorker
{
protected virtual void DoStuff(IEnumerable<Foo> foos)
{
foreach (var foo in foos)
{
Console.WriteLine(foo.Bar);
}
}
public Task Work(IEnumerable<Foo> foos) => Task.Run(() => DoStuff(foos));
}
public class FooWorker : AWorker { }
public class BarWorker : AWorker { }
You are firing off tasks and just forgetting them, while the thread continues running. This fixes it.
Main:
static async Task Main(string[] args)
{
var fooWorker = new FooWorker();
var barWorker = new BarWorker();
var test = new Class1(fooWorker, barWorker);
await test.SomeWork();
Console.ReadLine();
}
SomeWork:
public async Task SomeWork()
{
_foos = ProduceManyFoo();
await MoreWork();
Console.WriteLine("FINISHED !");
}
MoreWork signature change:
private async Task MoreWork()
The obvious code smell which should help make the problem clear is using async void. Unless required this should always be avoided.
When using async and await you'll usually want to chain the await calls to the top-level (in this case Main).
await is non-blocking, so anything that calls an async method should really care about the Task being returned.

Windows Form gets stuck on multiple async tasks

I am trying to execute parallel methods, but Form gets stuck whenever
I call them.
Please correct what I am doing wrong. Here is the Code:
public partial class Form1 : Form
{
private async void button1_Click(object sender, EventArgs e)
{
var itemList = new List<string>() { "Field1", "Field2", "Field3" };
await Task.WhenAll(itemList.Select(item =>
new WorkToDo(item).StartWork()
));
}
}
public class WorkToDo
{
private string id;
public WorkToDo(string id)
{
this.id = id;
}
public async Task<bool> StartWork()
{
Calculate();
Analyze();
SomeToDo();
var result = Save();
await Task.Delay(100);
return result;
}
private bool Calculate()
{
//Some complex and time taking calculation will be here
return true;
}
private bool Analyze()
{
//Some complex and time taking calculation will be here
return true;
}
private bool SomeToDo()
{
//Some complex and time taking calculation will be here
return true;
}
private bool Save()
{
//Some complex and time taking calculation will be here
return true;
}
}
You need to remember that normal async / await will still be performed on the UI thread.
So to be sure that a real long action is pushed to a background thread, you need to wrap it in Task.Run... like Task.Run(() => Task.WhenAll(tasks));
To complete the question a bit more ( seeing the other answer available ), Task.Run usage is not something to take lightly. It all depends on what sort of code needs to be wrapped.
There is a good write up series on this up on the blog of Stephen Cleary here http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-even-in.html so take some time to go through this and see what fits your project.
Or look here for some other details from Stephen https://stackoverflow.com/a/18015586
The problem that you have is that StartWork claims to be asynchronous, and yet it isn't. It does all of it's work synchronously.
Marking a method as async doesn't make it asynchronous. it just allows you to use the await keyword from within that method. If you perform long running synchronous operations from an async method then that method is still going to be doing that work synchronously.
There are really two approaches to take here. If there are some number of the things done in StartWork that really are inherently asynchronous, then you need to wrap whatever synchronous CPU bound work you have in calls to Task.Run so that the synchronous work that you have can be done asynchronously in a thread pool thread.
If there is no inherently asynchronous operations you have to do in StartWork then make the method clearly synchronous. Have it return a boolean, not a Task, and adjust the name to reflect the fact that it is synchronous. Then have the caller that calls it use Task.Run to do that whole operation on a thread pool thread asynchronously.
Having StartWork falsely claim to be asynchronous, and then still using Task.Run to do the purportedly asynchronous work in another thread is going to be extremely confusing to other readers of your code, as there should be no reason to offload an asynchronous method to a non-UI thread.
IMHO, If you are using Async operation you don't need Task.Run() if you have Sync Task and do it asynchronously you need Task.Run()
If you are using normal synchronous process just return Task<T> and use this Task.Run(()) to use background thread to process. See this answer
private async void button1_Click(object sender, EventArgs e)
{
var itemList = new List<string>() { "Field1", "Field2", "Field3" }; // more than 50 items
Task.Run(() => Task.WhenAll(tasks));
}
public class WorkToDo
{
private string id;
public WorkToDo(string id)
{
this.id = id;
}
public async Task<bool> StartWork()
{
var t1 = Calculate();
var t2 = Analyze();
var t3 = SomeToDo();
//Assuming you need to do all this before you save
// so wait for the all.
await Task.WhenAll(t1,t2,t3);
var result = await Save();
return result;
}
private async Task<bool> Calculate()
{
//Some complex and time taking calculation will be here
//Assuming here you have some DoAsync() method
return true;
}
private async Task<bool> Analyze()
{
//Some complex and time taking calculation will be here
return true;
}
private async Task<bool> SomeToDo()
{
//Some complex and time taking calculation will be here
return true;
}
private async Task<bool> Save()
{
//Some complex and time taking calculation will be here
return true;
}
Using WhenAll() has some advantage like propagating all error at once, see this

Is it safe to pass Task as method parameter?

Consider the following code:
public class Program {
static void Main(string[] args) {
Generate();
}
static void Generate() {
Task t = null;
t = Task.Run(() => {
MyClass myClass = new MyClass();
myClass.ContinueTask(t);
});
Console.ReadLine();
}
}
public class MyClass {
public void ContinueTask(Task t) {
t.ContinueWith(x => {
Console.WriteLine("Continue here...");
});
}
}
Is it safe to pass t as parameter as so or is it better to directly start a new task inside MyClass?
This is unsafe because t might not be assigned at the point where it is used. In fact this is a data race.
Even if you fixed that it would be bad architecture. Why does ContinueTask need to know that it is continuing with something. This is a not a concern that should be located here. ContinueTask should perform it's work assuming that its antecedent has completed already.
It's hard to tell what you are trying to accomplish. What's wrong with sequencing your code like this:?
static async Task Generate() {
var t = Task.Run(() => {
//... other code ...
});
MyClass myClass = new MyClass();
await t;
myClass.ContinueTask();
Console.ReadLine();
}
await is perfect for sequencing tasks.
reusing the Task object
What do you mean by that? A task cannot be reused. It cannot run twice. All that your ContinueWith does is logically wait for the antecedent and then run the lambda. Here, the task serves as an event basically.
ContinueWith does not modify the task it is being called on. It creates a new task.
I've reduced your code down to this example:
public Task<int> Parse()
{
Task<int> t = null;
t = Task.Run(() => this.Read(t));
return t;
}
public Task<int> Read(Task<int> t)
{
return t.ContinueWith(v => 42);
}
I think that has the same underlying structure.
This causes a dead-lock. I suspect your code does too. So I think it's unsafe.

Categories

Resources