What happens when calling an async function multiple times - c#

I have an async function that edits a database in C#
public async Task DeleteEntry(int Id)
{
var toDeleteEntry = (await firebase
.Child("Entries")
.OnceAsync<Entry>()).Where(a => a.Object.id == Id).FirstOrDefault();
await firebase.Child("Entries").Child(toDeleteEntry.Key).DeleteAsync();
}
If I call this function in a loop without having a wait function (commented out below)
foreach (Entry entry in allData)
{
var task = DeleteEntry(entry.id);
//task.Wait();
}
Will it:
Error out?
Create a new thread for each task?
Create only 1 thread and block until that thread finishes the next time it iterates the loop?
Please explain.

Not Throw any Exception. its Work Synchronously First Function Call Then Move To Next Iteration.
if you want to run in Thread Then use
public async Task DeleteEntry(int Id)
{
var toDeleteEntry = (await firebase
.Child("Entries")
.OnceAsync<Entry>()).Where(a => a.Object.id == Id).FirstOrDefault();
await firebase.Child("Entries").Child(toDeleteEntry.Key).DeleteAsync();
}
Want To Run With Task Then Use This Code. Its Execute The Method Parallels Thread
var task = Task.Run(() => DeleteEntry(entry.id));

Related

Task returning before its completion

I have desktop application in which I am doing some SQL Server queries asynchronously using ADO.NET. There is one case in which I am creating an object and its taking around 5-6 seconds for completion. Now the problem is occurring when I am calling a ADO.NET asynchronous method to fetch data from database. Once it hit await then call is return to main thread without completing other tasks and after some it returning back await but the call is already returned to main thread, due to this I am not getting complete data.
Here is a main caller:
Task.Run<Product>(() => product.GetProduct(item))
.ContinueWith(task =>
{
if (task.IsFaulted)
{
throw;
}
else if (task.Result != null)
{
// here it coming without completing a complete Task
}
})
.ConfigureAwait(false);
GetProduct methods do some more similar Task
public async Task<Product> GetProduct(ProductVariant item)
{
Product product = new Product();
product.Quantity = await GetQuantity(item.Id);
// some other properties
return product
}
The code when it hit it returned to the above task:
public async Task<List<Item>> Test()
{
// preparing query code
sqlDataReader = await sqlCommand.ExecuteReaderAsync();
// code logic after result returned
}
How can I make it so that returned a complete Task?
Based on the code and the comments you should await product.GetProduct(item) call plus I don't know the need of creating a Task (Task.Run and await it) just for it when you probably need the returned Product synchronously.
Your best bet is:
Product retProduct = await product.GetProduct(item);
if (retProduct != null)
{
// Do your logic
}
else
{
// Handle it
}

How do I stop awaiting a task but keep the task running in background?

If I have a list of tasks which I want to execute together but at the same time I want to execute a certain number of them together, so I await for one of them until one of them finishes, which then should mean awaiting should stop and a new task should be allowed to start, but when one of them finishes, I don't know how to stop awaiting for the task which is currently being awaited, I don't want to cancel the task, just stop awaiting and let it continue running in the background.
I have the following code
foreach (var link in SharedVars.DownloadQueue)
{
if (currentRunningCount != batch)
{
var task = DownloadFile(extraPathPerLink, link, totalLen);
_ = task.ContinueWith(_ =>
{
downloadQueueLinksTasks.Remove(task);
currentRunningCount--;
// TODO SHOULD CHANGE WHAT IS AWAITED
});
currentRunningCount++;
downloadQueueLinksTasks.Add(task);
}
if (currentRunningCount == batch)
{
// TODO SHOULD NOT AWAIT 0
await downloadQueueLinksTasks[0];
}
}
I found about Task.WhenAny but from this comment here I understood that the other tasks will be ignored so it's not the solution I want to achieve.
I'm sorry if the question is stupid or wrong but I can't seem to find any information related on how to solve it, or what is the name of the operation I want to achieve so I can even search correctly.
Solution Edit
All the answers provided are correct, I accepted the one I decided to use but still all of them are correct.
Thank you everyone, I learned a lot from all of you from these different answers and different ways to approach the problem and how to think about it.
What I learned about this specific problem was that I still needed to await for the other tasks left, so the solution was to have the Task.WhenAny inside the loop (which returns the finished task (this is also important)) AND Task.WhenAll outside the loop to await the other left tasks.
Task.WhenAny returns the Task which completed.
foreach (var link in SharedVars.DownloadQueue)
{
var task = DownloadFile(extraPathPerLink, link, totalLen);
downloadQueueLinksTasks.Add(task);
if (downloadQueueLinksTasks.Count == batch)
{
// Wait for any Task to complete, then remove it from
// the list of pending tasks.
var completedTask = await Task.WhenAny(downloadQueueLinksTasks);
downloadQueueLinksTasks.Remove(completedTask);
}
}
// Wait for all of the remaining Tasks to complete
await Task.WhenAll(downloadQueueLinksTasks);
You can use Task.WaitAny()
Here is the demonstration of the behavior:
public static async Task Main(string[] args)
{
IList<Task> tasks = new List<Task>();
tasks.Add(TestAsync(0));
tasks.Add(TestAsync(1));
tasks.Add(TestAsync(2));
tasks.Add(TestAsync(3));
tasks.Add(TestAsync(4));
tasks.Add(TestAsync(5));
var result = Task.WaitAny(tasks.ToArray());
Console.WriteLine("returned task id is {0}", result);
///do other operations where
//before exiting wait for other tasks so that your tasks won't get cancellation signal
await Task.WhenAll(tasks.ToArray());
}
public static async Task TestAsync(int i)
{
Console.WriteLine("Staring to wait" + i);
await Task.Delay(new Random().Next(1000, 10000));
Console.WriteLine("Task finished" + i);
}
Output:
Staring to wait0
Staring to wait1
Staring to wait2
Staring to wait3
Staring to wait4
Staring to wait5
Task finished0
returned task id is 0
Task finished4
Task finished2
Task finished1
Task finished5
Task finished3
What you're asking about is throttling, which for asynchronous code is best expressed via SemaphoreSlim:
var semaphore = new SemaphoreSlim(batch);
var tasks = SharedVars.DownloadQueue.Select(link =>
{
await semaphore.WaitAsync();
try { return DownloadFile(extraPathPerLink, link, totalLen); }
finally { semaphore.Release(); }
});
var results = await Task.WhenAll(tasks);
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:
IDisposable subscription =
SharedVars.DownloadQueue
.ToObservable()
.Select(link =>
Observable.FromAsync(() => DownloadFile(extraPathPerLink, link, totalLen)))
.Merge(batch) //max concurrent downloads
.Subscribe(file =>
{
/* process downloaded file here
(no longer a task) */
});
If you need to stop the downloads before they would naturally finish just call subscription.Dispose().

Async method within Task.Factory.StartNew wait all tasks before continuing

I have the following code:
public async Task<bool> ExecuteAsync()
{
return await someresult;
}
public async Task DoSomethingAsync()
{
var tasks = new List<Task>();
foreach (var item in someList)
{
var task = Task.Factory.StartNew(async () =>
{
await ExecuteAsync();
});
tasks.Add(task);
}
await Task.WhenAll(tasks);
// The program execution should stop here and next code should be run when all tasks are completed
}
Basically I am in a foreach loop where for each item I start a new Task.
Once all tasks are started I want the code after await Task.WhenAll(tasks); to be executed once all tasks are completed. Of course all are async therefore I am wondering if this is even possible? Because all are async the execution doesnt stop at Task.WhenAll(tasks) but continue even so not all tasks are completed. How can I fix this?
First of all, why you're wasting thread pools for for each task in the loop, your code could be written as:
public Task<bool> ExecuteAsync()
{
return ItReturnsATask();
}
public async Task DoSomethingAsync()
{
var tasks = new List<Task>();
foreach (var item in someList)
{
//ExecuteAsync can be replace by ItReturnsATask if there's no extra processing
tasks.Add(ExecuteAsync());
}
await Task.WhenAll(tasks);
}
Your code misses the point of asynchrony, It creates couple of threads from thread pool, and use them, these threads wasted here. As for your actual question, anything after await Task.WhenAll(tasks) doesn't get executed before all tasks are finished (assuming we're capturing the current context).

Asynchronous foreach

is there a way for an Asynchronous foreach in C#?
where id(s) will be processed asynchronously by the method, instead of using Parallel.ForEach
//This Gets all the ID(s)-IEnumerable <int>
var clientIds = new Clients().GetAllClientIds();
Parallel.ForEach(clientIds, ProcessId); //runs the method in parallel
static void ProcessId(int id)
{
// just process the id
}
should be something a foreach but runs asynchronously
foreach(var id in clientIds)
{
ProcessId(id) //runs the method with each Id asynchronously??
}
i'm trying to run the Program in Console, it should wait for all id(s) to complete processing before closing the Console.
No, it is not really possible.
Instead in foreach loop add what you want to do as Task for Task collection and later use Task.WaitAll.
var tasks = new List<Task>();
foreach(var something in somethings)
tasks.Add(DoJobAsync(something));
await Task.WhenAll(tasks);
Note that method DoJobAsync should return Task.
Update:
If your method does not return Task but something else (eg void) you have two options which are essentially the same:
1.Add Task.Run(action) to tasks collection instead
tasks.Add(Task.Run(() => DoJob(something)));
2.Wrap your sync method in method returning Task
private Task DoJobAsync(Something something)
{
return Task.Run(() => DoJob(something));
}
You can also use Task<TResult> generic if you want to receive some results from task execution.
Your target method would have to return a Task
static Task ProcessId(int id)
{
// just process the id
}
Processing ids would be done like this
// This Gets all the ID(s)-IEnumerable <int>
var clientIds = new Clients().GetAllClientIds();
// This gets all the tasks to be executed
var tasks = clientIds.Select(id => ProcessId(id)).
// this will create a task that will complete when all of the `Task`
// objects in an enumerable collection have completed.
await Task.WhenAll(tasks);
Now, in .NET 6 there is already a built-in Parallel.ForEachAsync
See:
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel.foreachasync?view=net-6.0
https://www.hanselman.com/blog/parallelforeachasync-in-net-6

C# tasks are executed before Task.WhenAll

Why the tasks are executed before Task.WhenAll??
If you see here, from the below code snippet, first Console.WriteLine("This should be written first.."); should be printed because I am awaiting the tasks beneath to it..
But if you see the output result, the Tasks method result is being printed before the above statement. Ideally, the tasks method should be executed when I await them, but it seems that- the tasks methods are executed the moment I add them in tasks list. Why is it so?
Would you please do let me know why is this happening??
Code:
public static async Task Test()
{
var tasks = new List<Task>();
tasks.Add(PrintNumber(1));
tasks.Add(PrintNumber(2));
tasks.Add(PrintNumber(3));
Console.WriteLine("This should be written first..");
// This should be printed last..
await Task.WhenAll(tasks);
}
public static async Task PrintNumber(int number)
{
await Task.FromResult(0);
Console.WriteLine(number);
}
Output
When you call an async method you get a "hot" task in return. That means that the task already started running (and maybe even completed) before you get to await them. That means that it's quite possible for the tasks to run and complete before the call to Task.WhenAll.
In your case however, while the PrintNumber is marked async it isn't asynchronous at all since you're using Task.FromResult. The synchronous part of an asynchronous method (which is the part until you await an asynchronous task) is always executed synchronously on the calling thread and is done before the call returns. When you use Task.FromResult you get a completed task so all your method is just the synchronous part and is completed before the call returns.
When you await a completed task (as is created by Task.FromResult, it completes synchronously. This means that in your example, nothing is actually happening asynchronously, which explains the order of execution.
If instead, you were to
await Task.Yield();
you'd see output more in line with your expectations.
Task.FromResult won't cause yield and the task will be executed on the same thread. To achieve what you want you can do this:
public static async Task Test()
{
var tasks = new List<Task>();
tasks.Add(PrintNumber(1));
tasks.Add(PrintNumber(2));
tasks.Add(PrintNumber(3));
Console.WriteLine("This should be written first..");
// This should be printed last..
await Task.WhenAll(tasks);
}
public static async Task PrintNumber(int number)
{
await Task.Yield();
Console.WriteLine(number);
}
If you want a Task or tasks to run after something else, its easiest to write your code accordingly.
public static async Task Test()
{
Console.WriteLine("This should be written first..");
// These should be printed last..
await Task.WhenAll(new[]
{
PrintNumber(1),
PrintNumber(2),
PrintNumber(3)
});
}
following on from your comment.
So we have some functions,
async Task<Customer> GetRawCustomer()
{
...
}
async Task<string> GetCity(Customer customer)
{
...
}
async Task<string> GetZipCode(Customer customer)
{
...
}
We could use them like this
var rawCustomer = await GetRawCustomer();
var populationWork = new List<Task>();
Task<string> getCity;
if (string.IsNullOrWhiteSpace(rawCustomer.City))
{
getCity = GetCity(rawCustomer);
populationWork.Add(getCity);
}
Task<string> getZipCode;
if (string.IsNullOrWhiteSpace(rawCustomer.City))
{
getZipCode = GetZipCode(rawCustomer);
populationWork.Add(getZipCode);
}
...
await Task.WhenAll(populationWork);
if (getCity != null)
rawCustomer.City = getCity.Result;
if (getZipCode != null)
rawCustomer.ZipCode = getZipCode.Result;

Categories

Resources