Advice on two implementations of async/await - c#

I noticed that I am able to get the data in the following two ways (reference OPTION 1 and OPTION 2) and wanted to know if there are any implications/benefits in picking one over the other? I am using .NET 4.5.1 and am loading data from an on-premise SQL Server using async/await.
// ***** OPTION 1 *****
// Constructor
public SomeViewModel(IProjectService projectService)
{
Task.Run(async () => await GetAllDataAsync());
}
// This method calls out all of the other local async/await methods to download the data
private async Task GetAllDataAsync()
{
await GetProjectDataAsync();
await GetAssignmentDataAsync();
... more await calls ...
}
// A typical method call to get data from service
private async Task GetProjectDataAsync()
{
var projects = await projectService.GetProjectsAsync();
this.ProjectList = projects;
}
// ***** OPTION 2 *****
// Constructor
// I call Task.Run() for all of the methods that download data
public SomeViewModel()
{
_service = service;
Task.Run(async () => await this.GetProjectDataAsync());
Task.Run(async () => await this.GetAssignmentDataAsync());
... more Task.Run() calls ...
}
// A typical method call to get data from service
private async Task GetProjectDataAsync()
{
var projects = await projectService.GetProjectsAsync();
this.ProjectList = projects;
}
EDIT 1:
Here is an example of the async/await method from my repository:
public async Task<IList<Project>> GetProjectsAsync()
{
using (Context ctx= new ProjectContext())
{
IEnumerable<Project> myProjects = ctx.Projects;
return await myProjects.ToListAsync();
}
}
EDIT 2:
I currently acquired the book, Concurrency in C# Cookbook, by Stephen Cleary and am looking for a suitable solution from the book in combination with the given advice here. Could anyone point me to certain section of the book that can assist me in my scenario, i.e. loading a lot of data from many tables that will be harnessed by an application?
EDIT 3:
// ***** OPTION 3 *****
// Constructor
public SomeViewModel()
{
_service = service;
DownloadData();
}
// A normal method that awaits async methods
private void DownloadData()
{
Task.Run(async () => await GetProjectDataAsync());
Task.Run(async () => await GetAssignmentDataAsync());
... more await calls to async Task methods that call services that in turn have async Task methods
}

I doubt any of these are suitable for your needs. Since a constructor can't be async there is no way to wait until the task you start are complete. You could call Task.Wait but that would annihilate all the benefits derived from async and you'd need to deal with the resulting deadlock.
Right now the tasks you start run concurrently with the rest of the request processing. This is probably racy and not what you wanted. If this ever worked it was by accident.
Move that code out of the ctor to an ordinary method and use await all the way through.

Related

Trigger Task method rapidly

Command receiver:
public override async ValueTask Execute(){
// my code here
// this is boardcast command
// attempt #1
Parallel.ForEach(lists, async list => { await DoSomething(); });
// attempt #2
List<Task> tasks = new List<Task>();
foreach(ulong id in ids) tasks.Add(DoSomething());
await Task.WhenAll(tasks);
}
DoSomething():
public async Task DoSomething(){
Task<abc> Request = API.RequestSomething();
// attempt #1
await Request.ContinueWith(Callback => {
// Do something (long time...) , eg: update database, update all client data etc.
});
// attempt #2
await Request.ContinueWith(Callback => {
// .....
}, TaskContinuationOptions.ExecuteSynchronously);
}
Question is : If client sent command to receiver rapidly (about 100 commands per seconds), how can I handle this without (or low) delay, or how can I make my task function (Task DoSomeThing();) run in parallel
I have tested my code and it works fine. But I would like your advice if there is a better way. Or is there any mistake that I'm missing? (I haven't tested it with multiple commands per second).
As others have noted, Parallel should not be used with async methods, so the Task.WhenAll approach is the correct solution for asynchronous concurrency:
public override async ValueTask Execute() {
var tasks = ids.Select(id => DoSomething()).ToList();
await Task.WhenAll(tasks);
}
Regarding your implementation, do not use ContinueWith:
public async Task DoSomething() {
var Callback = await API.RequestSomething();
// Do something (long time...) , eg: update database, update all client data etc.
}

How can I await until I receive a callback/notification without busy-waiting?

I understand how I can await on library code to wait for a network request or other long-running action to complete, but how can I await on my own long-running action without busy waiting?
This is the busy-waiting solution. How can I make it event-driven?
public async Task<DataBlock> ClickSend() {
// await until someone calls DataReceived()
while (_Block == null) {
await Task.Yield();
}
return _Block;
}
// Something external calls this function.
// Once called, ClickSend should complete with this data.
public void DataReceived(DataBlock data_block) {
_Block = data_block;
}
DataBlock _Block = null;
(This question is completely different from How do I await events in C#? which is asking how to await on event handlers, but very similar to Promise equivalent in C#.)
Generally in concurrency a "future" is placeholder for a return value and it has an associated "promise" that is fulfilled to pass the final return value.
In C#, they have different names: the future is a Task and the promise is a TaskCompletionSource.
You can create a promise, await on it, and then fulfill it when you get your callback:
TaskCompletionSource<DataBlock> _Promise = new TaskCompletionSource<DataBlock>();
public async Task<DataBlock> ClickSend() {
DataBlock block = await _Promise.Task;
return block;
}
public void DataReceived(DataBlock data_block) {
_Promise.TrySetResult(data_block);
}
Here's an executable example.

Why to use Async inside Task.Run?

While working on some code, I came across the following,
Approach 1:
private void MyMethod()
{
var t = Task.Run(
async () =>
{
Foo.Fim();
await Task.Delay(5000);
});
t.Wait();
}
We deliberately introduce a delay of 5 seconds to allow Fim() to complete its work. It is required to introduce the delay - imagine this to be calling a third party API which mandates a cooling period of 5 seconds.
I wanted to understand if this the right approach to wait for an operation to complete. How is Approach 1 different from the following Approach 2? Or is there a better way to do this? All we need is to have the cooling period delay and avoid blocking the UI.
Approach 2:
private void MyMethod()
{
var t = Task.Run(
() =>
{
Foo.Fim();
Task.Delay(5000).Wait(); // some operation which takes 5 seconds.
});
t.Wait();
}
What you need is a throttler, and a handy class for implementing one is the SemaphoreSlim:
Limits the number of threads that can access a resource or pool of resources concurrently.
private SemaphoreSlim _myMethodSemaphore = new SemaphoreSlim(1);
private void MyMethod()
{
_ = Task.Run(async () =>
{
await _myMethodSemaphore.WaitAsync();
try
{
Foo.Fim();
}
finally
{
await Task.Delay(5000); // Impose the delay even in case of an exception
_myMethodSemaphore.Release();
}
});
}
Note that there is no Wait() in this example. The only blocking call is the Foo.Fim() method call. By making this method asynchronous you could have a perfect solution regarding scalability: await Foo.FimAsync()

Nested await or async's "glutting"

Let's consider the next procedures hierarhy
Main.cs:
// timer callback
{
Plot.UpdateData();
}
Plot.cs:
public async void UpdateData()
{
await CalculateData();
// ...
}
private async Task CalculateData()
{
await Calculations.Calculate();
// UI updating
// ...
}
Calculations.cs:
public static async Task<bool> Calculate()
{
async Task<bool> CalculateLR()
{
var task1 = Calculate1();
var task2 = Calculate2();
await Task.WhenAll(new[] { task1, task2 });
return true;
}
var leftTask = CalculateLR();
var rightTask = CalculateLR();
await Task.WhenAll(new[] { leftTask, rightTask });
await Calculate3();
return true;
}
Here I have some basic calculations (in Calculate1-Calculate3 procedures) of Calculations.cs file and some interaction with UI. The "entry point" Plot.UpdateData is placed in Device.StartTimer( block of the main form.
It works, but I think this structure creates excess threads. So my question is can this hierarhy be optimized without loss of asynchronous advantages?
Or, other words, which procedures should be awaited in case of nested calls. Where is first non-awaited call should lie? Thanks.
First thing to note: async/await is about tasks, not threads. Under certain circumstances, a task can be thread equivalent, and in most cases it is not (the same thread can serve a lot of tasks sequentially conveyor-style, depending on how they're scheduled for continuation and what is awaiting condition, for example).
And I could strongly recommend to address this for further reading as very comprehensive source:
https://blog.stephencleary.com/2013/11/there-is-no-thread.html
https://blog.stephencleary.com/2014/05/a-tour-of-task-part-1-constructors.html

async / await - am I correctly running these methods in parallel?

I have an abstract class called VehicleInfoFetcher which returns information asynchronously from a WebClient via this method:
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID);
I'd like to combine the results of two separate instances of this class, running each in parallel before combining the results. This is done within a third class, CombinedVehicleInfoFetcher (also itself a subclass of VehicleInfoFetcher)
Here's my code - but I'm not quite convinced that it's running the tasks in parallel; am I doing it right? Could it be optimized?
public class CombinedVehicleInfoFetcher : VehicleInfoFetcher
{
public HashSet<VehicleInfoFetcher> VehicleInfoFetchers { get; set; }
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID)
{
// Create a list of parallel tasks to run
var resultTasks = new List<Task<DTOrealtimeinfo>>();
foreach (VehicleInfoFetcher fetcher in VehicleInfoFetchers)
resultTasks.Add(fetcher.getVehicleInfo(stopID, stopID2, timePointLocal));
// run each task
foreach (var task in resultTasks)
await task;
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
var allRealtimeResults = new List<DTOrealtimeinfo>( resultTasks.Select(t => t.Result) );
return combineTaskResults(allRealtimeResults);
}
DTOrealtimeinfo combineTaskResults(List<DTOrealtimeinfo> realtimeResults)
{
// ...
return rtInfoOutput;
}
}
Edit
Some very helpful answers, here is a re-written example to aid discussion with usr below:
public override async Task<object> combineResults()
{
// Create a list of parallel tasks to run
var resultTasks= new List<object>();
foreach (AnotherClass cls in this.OtherClasses)
resultTasks.Add(cls.getResults() );
// Point A - have the cls.getResults() methods been called yet?
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
return new List<object>( resultTasks.Select(t => t.Result) );
}
}
Almost all tasks start out already started. Probably, whatever fetcher.getVehicleInfo returns is already started. So you can remove:
// run each task
foreach (var task in resultTasks)
await task;
Task.WhenAll is faster and has better error behavior (you want all exceptions to be propagated, not just the first you happen to stumble upon).
Also, await does not start a task. It waits for completion. You have to arrange for the tasks to be started separately, but as I said, almost all tasks are already started when you get them. This is best-practice as well.
To help our discussion in the comments:
Task Test1() { return new Task(() => {}); }
Task Test2() { return Task.Factory.StartNew(() => {}); }
Task Test3() { return new FileStream("").ReadAsync(...); }
Task Test4() { return new TaskCompletionSource<object>().Task; }
Does not "run" when returned from the method. Must be started. Bad practice.
Runs when returned. Does not matter what you do with it, it is already running. Not necessary to add it to a list or store it somewhere.
Already runs like (2).
The notion of running does not make sense here. This task will never complete although it cannot be explicitly started.

Categories