How to write an asynchronous method WITHOUT using Task.Delay OR await - c#

I'm trying to learn to write my own asynchronous methods, but I'm having difficulty, because ALL of the millions of examples that I have seen online ALL use await Task.Delay inside the custom async method and I neither want to add a delay into my code, nor have any other async method to call in its place.
Let's use a simple example, where I want to create a new collection of objects, with only two properties, from a huge existing collection of objects, that each have a great many properties. Let's say this is my synchronous code:
public List<SomeLightType> ToLightCollection(List<SomeType> collection)
{
List<SomeLightType> lightCollection = new()
foreach (SomeType item in collection)
{
lightCollection.Add(new SomeLightType(item.Id, item.Name));
}
return lightCollection;
}
To make this method asynchronous, do I just need to wrap it in a Task.Run, add the async keyword and suffix on the method name, and change the return type, as follows?:
public Task<List<SomeLightType>> ToLightCollectionAsync(List<SomeType> collection)
{
List<SomeLightType> lightCollection = new()
Task.Run(() =>
{
foreach (SomeType item in collection)
{
lightCollection.Add(new SomeLightType(item.Id, item.Name));
}
});
return lightCollection;
}
Or do I also need to await the return of the Task inside the method? (The compiler gave me a warning until I added await.):
public async Task<List<SomeLightType>> ToLightCollectionAsync(List<SomeType> collection)
{
List<SomeLightType> lightCollection = new()
await Task.Run(() =>
{
foreach (SomeType item in collection)
{
lightCollection.Add(new SomeLightType(item.Id, item.Name));
}
});
return lightCollection;
}
EDIT:
Oh yes, I have just realised that I need to await this operation, otherwise the empty collection will be returned before it is populated. But still, is this the correct way to make this code run asynchronously?

ALL of the millions of examples that I have seen online ALL use await Task.Delay inside the custom async method and I neither want to add a delay into my code, nor have any other async method to call in its place.
Task.Delay is commonly used as a "placeholder" meaning "replace this with your actual asynchronous work".
I'm trying to learn to write my own asynchronous methods
Asynchronous code begins at the "other end". The most common example is with an I/O operation: you can make this asynchronous instead of blocking the calling thread. At the lowest level, this is commonly done using a TaskCompletionSource<T>, which creates a Task<T> you can return immediately, and then later when the operation completes, you can use the TaskCompletionSource<T> to complete the Task<T>.
However, as you state in the comments:
I definitely want that method to run asynchronously, as it currently takes several minutes... this is a WPF application
What you really want is not asynchronous code; you want to run some code on a background thread so it doesn't block the UI thread. The code being run is CPU-bound and has no I/O to do, so it's just going to run on a thread pool thread instead of actually being asynchronous.
Let's use a simple example... To make this method asynchronous...
To run this code on a background thread, you would use Task.Run. However, I recommend that you do not implement this method using Task.Run. If you do, then you have a method that looks asynchronous but is not actually asynchronous; it's just running synchronously on a thread pool thread - what I call "fake asynchronous" (it has an asynchronous signature but is not actually asynchronous).
IMO, it's cleaner to keep your business logic synchronous, and in this case since you want to free up the UI thread, have the UI code call it using Task.Run:
// no change
public List<SomeLightType> ToLightCollection(List<SomeType> collection)
{
List<SomeLightType> lightCollection = new()
foreach (SomeType item in collection)
{
lightCollection.Add(new SomeLightType(item.Id, item.Name));
}
return lightCollection;
}
async void Button_Click(...)
{
var source = ...
var lights = await Task.Run(() => ToLightCollection(source));
... // Do something with lights
}

Task.Run is for CPU-Bound work (see learn.microsoft.com - Async in depth.
You can avoid await Task.Run() scenarios if you return the created task directly:
public Task<List<SomeLightType>> ToLightCollectionAsync(List<SomeType> collection) => Task.Run(() =>
{
List<SomeLightType> lightCollection = new();
// Do CPU bound work
foreach (SomeType item in collection)
{
lightCollection.Add(new SomeLightType(item.Id, item.Name));
}
return lightCollection;
});
Now the caller can await for the result in an async Method to keep your UI responsive:
public async Task CallerMethod()
{
// ...
var result = await ToLightCollectionAsync(collection);
}
You also have the opportunity to perform some work during this computation.
public async Task CallerMethod()
{
var task = ToLightCollectionAsync(collection);
// Do some other work
var result = await task;
}

Related

ForEach lambda async vs Task.WhenAll

I have an async method like this:
private async Task SendAsync(string text) {
...
}
I also have to use this method one time for each item in a List:
List<string> textsToSend = new Service().GetMessages();
Currently my implementation is this:
List<string> textsToSend = new Service().GetMessages();
List<Task> tasks = new List<Task>(textsToSend.Count);
textsToSend.ForEach(t => tasks.Add(SendAsync(t)));
await Task.WhenAll(tasks);
With this code, I get a Task for each message that runs async the sending method.
However, I don't know if is there any different between my implementation and this one:
List<string> textsToSend = new Service().GetMessages();
textsToSend.ForEach(async t => await SendAsync(t));
In the second one, I don't have the List<Task> allocation, but I think that the first one launch all Task in parallel and the second sample, one by one.
Could you help me to clarify if is there any different between the first and second samples?
PD: I also know that C#8 supports foreach async, however I'm using C# 7
You don't even need a list, much less ForEach to execute multiple tasks and await all of them. In any case, ForEach is just a convenience function that uses `foreach.
To execute some async calls concurrently bases on a list of inputs all you need is Enumerable.Select. To await all of them to complete you only need Task.WhenAll :
var tasks=textsToSend.Select(text=>SendAsync(text));
await Task.WhenAll(tasks);
LINQ and IEnumerable in general use lazy evaluation which means Select's code won't be executed until the returned IEnumerable is iterated. In this case it doesn't matter because it's iterated in the very next line. If one wanted to force all tasks to start a call to ToArray() would be enough, eg :
var tasks=textsToSend.Select(SendAsync).ToArray();
If you wanted to execute those async calls sequentially, ie one after the other, you could use a simple foreach. There's no need for C# 8's await foreach :
foreach(var text in textsToSend)
{
await SendAsync(text);
}
The Bug
This line is simply a bug :
textsToSend.ForEach(async t => await SendAsync(t));
ForEach doesn't know anything about tasks so it never awaits for the generated tasks to complete. In fact, the tasks can't be awaited at all. The async t syntax creates an async void delegate. It's equivalent to :
async void MyMethod(string t)
{
await SendAsync(t);
}
textToSend.ForEach(t=>MyMethod(t));
This brings all the problems of async void methods. Since the application knows nothing about those async void calls, it could easily terminate before those methods complete, resulting in NREs, ObjectDisposedExceptions and other weird problems.
For reference check David Fowler's Implicit async void delegates
C# 8 and await foreach
C# 8's IAsyncEnumerable would be useful in the sequential case, if we wanted to return the results of each async operation in an iterator, as soon as we got them.
Before C# 8 there would be no way to avoid awaiting for all results, even with sequential execution. We'd have to collect all of them in a list. Assuming each operation returned a string, we'd have to write :
async Task<List<string> SendTexts(IEnumerable<string> textsToSend)
{
var results=new List<string>();
foreach(var text in textsToSend)
{
var result=await SendAsync(text);
results.Add(result);
}
}
And use it with :
var results=await SendTexts(texts);
In C# 8 we can return individual results and use them asynchronously. We don't need to cache the results before returning them either :
async IAsyncEmumerable<string> SendTexts(IEnumerable<string> textsToSend)
{
foreach(var text in textsToSend)
{
var result=await SendAsync(text);
yield return;
}
}
await foreach(var result in SendTexts(texts))
{
...
}
await foreach is only needed to consume the IAsyncEnumerable result, not produce it
that the first one launch all Task in parallel
Correct. And await Task.WhenAll(tasks); waits for all messages are sent.
The second one also sends messages in parallel but doesn't wait for all messages are sent since you don't await any task.
In your case:
textsToSend.ForEach(async t => await SendAsync(t));
is equivalent to
textsToSend.ForEach(t => SendAsync(t));
the async t => await SendAsync(t) delegate may return the task (it depends on assignable type) as SendAsync(t). In case of passing it to ForEach both async t => await SendAsync(t) and SendAsync(t) will be translated to Action<string>.
Also the first code will throw an exception if any SendAsync throws an excepion. In the second code any exception will be ignored.

Threading foreach that abides by async/await methods inside said foreach

I have been looking up a way to itterate through a foreach with multiple threads, for example, let's say I have a list
public List<MYclass> All()
{
// fill the list
}
Private async Task Main()
{
foreach(All() as whatever)
{
await method();
await method2();
}
}
private async Task method()
{
//do some stuff
// more stuff
await another();
}
private async Task method2()
{
//do some stuff
// more stuff
await another2();
}
private async Task another()
{
//await client to do whatever
}
private async Task another2()
{
//await client to do whatever
}
I am trying to do the following:
List item 1 = thread1
List item 2 = thread2
List item 3 = thread3
...etc depending on how many threads I have
I've been looking around with no hope, I found Parrellel.foreach, but that doesn't wait for awaits, because once it hits the await, it complete the whole action then starts the foreach again, so how could I go about achieving what I want? Any help would be greatly apreciated.
The first thing to say, is that async/await code isn't really multi threaded . I think it sort of creates another thread sometimes, but its mainly meant to alleviate blocking in code.
I tend to think mixing multithreading and async is actually a bit of a pain because exception handling is a bit harder and less predictable. You'd be better off using Parallel foreach, and taking async off the method call definitions. Or just calling .Result inside the parallel foreach to make that code execute synchronously.
"Threads" are too low-level a concept to think about these days - after all, when a method is awaiting, it doesn't even use a thread. Instead, you want to think about "tasks" and "operations".
In this case, what you want is asynchronous concurrency (not parallel/threaded concurrency). First, define the (single) operation that you want done for each item:
private async Task ProcessAsync(MyClass item)
{
await method();
await method2();
}
Next, to start operations for all tasks concurrently, you can use Select:
private async Task Main()
{
var tasks = All().Select(ProcessAsync).ToArray();
}
and then, to (asynchronously) wait for them all to complete, use await Task.WhenAll:
private async Task Main()
{
var tasks = All().Select(ProcessAsync).ToArray();
await Task.WhenAll(tasks);
}

How to wait the result of async operations without await?

void A()
{
foreach (var document in documents)
{
var res = records.BulkWriteAsync(operationList, writeOptions); // res is Task<BulkWriteResult<JobInfoRecord>>
}
}
After foreach I would like to wait the result of all BulkWriteAsync, how to do this? I don't want to mark A() as async and do the following
await records.BulkWriteAsync(operationList, writeOptions);
Is it good solution?
void A()
{
var tasks = new List<Task<BulkWriteResult<JobInfoRecord>>>();
foreach (var document in documents)
{
var task = records.BulkWriteAsync(operationList, writeOptions);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
I call A() in try catch if I will mark public async void A() as async I never be in catch
Well, first you want a Task that represents all the operations. The simplest way to do this is with a bit of LINQ:
Task.WhenAll(documents.Select(i => records.BulkWriteAsync(...)));
Then, you ideally want to await that task. If that isn't possible, you can try
task.GetAwaiter().GetResult();
However, make sure that none of the tasks have thread affinity - that's a great way to get a deadlock. Waiting for a task on the UI thread while the task itself needs the UI thread is a typical example.
The whole point of await is that it allows you to handle asynchronous code as if it were synchronous. So from the outside, it appears as if you never left the method until you actually get to a return (or the end of the method). For this to work, however, your method must return a Task (or Task<T>), and the callee must await your method in turn.
So a code like this:
try
{
tasks = Task.WhenAll(documents.Select(i => ...));
await tasks;
}
catch (Exception ex)
{
// Handle the exception
}
will appear to run completely synchronously, and all exceptions will be handled as usual (though since we're using Task.WhenAll, some will be wrapped in AggregateException).
However, this isn't actually possible to handle with the way .NET and C# is built, so the C# compiler cheats - await is basically a return that gives you a promise of the result you'll get in the future. And when that happens, the control returns back to where the await left the last time. Task is that promise - if you use async void, there's no way for the callee to know what's happening, and it has no option but to continue as if the asynchronous method was a run-and-forget method. If you use async Task, you can await the async method and everything "feels" synchronous again. Don't break the chain, and the illusion is perfect :)

Nested async lambda not awaited

The following code does not return the entire collection it is iterating. The returned array has an arbitrary length on every run. What's wrong?
public async Task<IHttpActionResult> GetClients()
{
var clientInfoCollection = new ConcurrentBag<ClientInfoModel>();
await _client.Iterate(async (client) =>
{
clientInfoCollection.Add(new ClientInfoModel
{
name = client.name,
userCount = await _user.Count(clientId)
});
});
return Ok(clientInfoCollection.ToArray());
}
The following code uses the new async MongoDB C# driver
public async Task Iterate(Action<TDocument> processor)
{
await _collection.Find<TDocument>(_ => true).ForEachAsync(processor);
}
The reason you're seeing arbitrary number of values is in the fact the Iterate receives a delegate of type Action<T>, which is equivalent to async void, effectively making this a "fire-and-forget" style of execution.
The inner method isn't actually aware that an async delegate has been passed to it, hence it iterates the collection without actually asynchronously waiting for each item to complete.
What you need to do instead is make the method parameter a delegate of type Func<TDocument, Task> and use the proper overload of ForEachAsync:
public Task Iterate(Func<TDocument, Task> processor)
{
return _collection.Find<TDocument>(_ => true).ForEachAsync(processor);
}
You can see the source here:
public static async Task ForEachAsync<TDocument>(
this IAsyncCursor<TDocument> source,
Func<TDocument, int, Task> processor,
CancellationToken cancellationToken = default(CancellationToken))
{
Ensure.IsNotNull(source, "source");
Ensure.IsNotNull(processor, "processor");
// yes, we are taking ownership... assumption being that they've
// exhausted the thing and don't need it anymore.
using (source)
{
var index = 0;
while (await source.MoveNextAsync(cancellationToken).ConfigureAwait(false))
{
foreach (var document in source.Current)
{
await processor(document, index++).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
}
}
}
}
You create the threads, and set them off. From there you can't know what happens. But your codes next step is to return, so you are gambling that the threads will execute faster, than your main thread.
In normal threading scenarios, you will join the threads, who are adding items to the bag. Where a join, is the threads, waiting for the other threads to execute and thereby still being async, but waiting to return before everything is completed.
Which is perfectly explained here: http://www.dotnetperls.com/thread-join

Did I actually gain anything here using tasks and async

I am trying to get to grips with Tasks and the Async Await keywords. I have a little sample method which essentially invokes n number of methods. Two key points to note are
I don't care about the order the methods run in
All methods should be called on a background thread
With this here is the code.
public async void Handle<T>(T entry) {
await Task.Run(() => {
Parallel.ForEach(_handlers, pair => {
pair.Value.Invoke(_reference, new object[] {
entry
});
});
});
My question is did I actually gain any async or parallelism out of the code above?
I'm assuming that you're running in a UI application, since parallel code on a server is quite rare.
In this case, wrapping parallel code in a Task.Run is perfectly normal, and a common pattern when you wish to execute parallel code asynchronously.
I would make a few changes:
Avoid async void. Return a Task, so you can handle errors more cleanly.
Follow the Task-based Asynchronous Pattern naming conventions (i.e.,
end your method with Async).
Return the Task directly instead of async/await (if your entire async method is just to await a task, you can avoid some overhead just by returning the task directly).
Like this:
public Task HandleAsync<T>(T entry) {
return Task.Run(() => {
Parallel.ForEach(_handlers, pair => {
pair.Value.Invoke(_reference, new object[] {
entry
});
});
});
I'd also consider two other possibilities:
Consider using Parallel.Invoke instead of Parallel.ForEach. It seems to me that Parallel.Invoke is a closer match to what you're actually trying to do. (See svick's comment below).
Consider leaving the parallel code in a synchronous method (e.g., public void Handle<T>(T entry)) and calling it with Task.Run (e.g., await Task.Run(() => Handle(entry));). If your Handle[Async] method is intended to be part of a general-purpose library, then you want to avoid the use of Task.Run within your asynchronous methods.
No, you've gained nothing here. Even if you made it return Task (so that the caller could check when everything had finished) you'd be better off without async:
public Task Handle<T>(T entry)
{
return Task.Run(() =>
{
Parallel.ForEach(_handlers, pair =>
{
pair.Value.Invoke(_reference, new object[] { entry });
});
});
}

Categories

Resources