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

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

Related

C# async lock get the same result without code execution

I have a method that returns some value based on an API call, this API limits the amount of calls that you can do per period of time. I need to access the results of this call from multiple threads. Right now i have the following code:
class ReturningSemaphoreLocker<TOutput>
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
public async Task<T> LockAsync<T>(Func<Task<T>> worker)
{
await _semaphore.WaitAsync();
try
{
return await worker();
}
finally
{
_semaphore.Release();
}
}
}
Usage example:
...
private static readonly ReturningSemaphoreLocker<List<int>> LockingSemaphore = new ReturningSemaphoreLocker<List<int>>();
...
public async Task<List<int>> GetStuff()
{
return await LockingSemaphore.LockAsync(async () =>
{
var client = _clientFactory.CreateClient("SomeName");
using (var cts = GetDefaultRequestCts())
{
var resp = await client.GetAsync("API TO QUERY URL", cts.Token);
var jsonString = await resp.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<List<int>>(jsonString);
return items;
}
});
}
So the question is: how do i get the same result from GetStuff() if it's already running WITHOUT querying the API again and query the API again if the method is not running at this very moment?
The trick here is to hold onto the Task<T> that is the incomplete result; consider the following completely untested approach - the _inProgress field is the key here:
private static readonly ReturningSemaphoreLocker<List<int>> LockingSemaphore = new ReturningSemaphoreLocker<List<int>>();
...
private Task<List<int>> _inProgress;
public Task<List<int>> GetStuffAsync()
{
if (_inProgress != null) return _inProgress;
return _inProgress = GetStuffImplAsync();
}
private async Task<List<int>> GetStuffImplAsync()
{
var result = await LockingSemaphore.LockAsync(async () =>
{
var client = _clientFactory.CreateClient("SomeName");
using (var cts = GetDefaultRequestCts())
{
var resp = await client.GetAsync("API TO QUERY URL", cts.Token);
var jsonString = await resp.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<List<int>>(jsonString);
return items;
}
});
// this is important so that if everything turns
// out to be synchronous, we don't nuke the _inProgress field *before*
// it has actually been set
await Task.Yield();
// and now wipe the field since we know it is no longer in progress;
// the next caller should actually try to do something interesting
_inProgress = null;
return result;
}
Here is a class that you could use for time-based throttling, instead of the ReturningSemaphoreLocker:
class ThrottledOperation
{
private readonly object _locker = new object();
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
private Task _task;
public Task<T> GetValueAsync<T>(Func<Task<T>> taskFactory, TimeSpan interval)
{
lock (_locker)
{
if (_task != null && (_stopwatch.Elapsed < interval || !_task.IsCompleted))
{
return (Task<T>)_task;
}
_task = taskFactory();
_stopwatch.Restart();
return (Task<T>)_task;
}
}
}
The GetValueAsync method returns the same task, until the throttling interval has been elapsed and the task has been completed. At that point it creates and returns a new task, using the supplied task-factory method.
Usage example:
private static readonly ThrottledOperation _throttledStuff = new ThrottledOperation();
public Task<List<int>> GetStuffAsync()
{
return _throttledStuff.GetValueAsync(async () =>
{
var client = _clientFactory.CreateClient("SomeName");
using (var cts = GetDefaultRequestCts())
{
var resp = await client.GetAsync("API TO QUERY URL", cts.Token);
var jsonString = await resp.Content.ReadAsStringAsync();
var items = JsonConvert.DeserializeObject<List<int>>(jsonString);
return items;
}
}, TimeSpan.FromSeconds(30));
}

Async seems to be synchronous

I am not sure if I am missing something here but more for loop seems to be executing synchronously even though I await all tasks out side of it.
Here is my code below:
static void Main(string[] args) {
var t = Start();
}
public static async Task < List < Task < TaskInfo >>> Start() {
var listOfTasks = new List < Task < TaskInfo >> ();
for (var i = 0; i <= 100; i++) {
var process = new Processor();
listOfTasks.Add(process.Process(i));
}
await Task.WhenAll(listOfTasks);
return listOfTasks;
}
I pass in the taskId to log out just to see the order the tasks execute.
Am I missing something really obvious here?
EDIT:
Changed code to this based on the answers and comments below and it still appears synchronously:
public class StartWork
{
public int TaskId { get; set; }
public Processor Processor { get;}
public StartWork()
{
Processor = new Processor();
}
}
static void Main(string[] args)
{
var t = Start();
}
public static async Task<TaskInfo[]> Start()
{
var tasks = new List<StartWork>();
for (int i = 1; i < 100; i++)
{
var work = new StartWork
{
TaskId = i
};
tasks.Add(work);
}
return await Task.WhenAll(tasks.Select(i => i.Processor.Process(i.TaskId)));
}
The function I am calling in processor class:
public Task<TaskInfo> Process(int taskId)
{
try
{
taskId = taskId + 1;
stopwatch.Start();
using (var bus = RabbitHutch.CreateBus(xxDev))
{
#event = new AutoResetEvent(false);
var replyTo = Guid.NewGuid().ToString();
var messageQueue = bus.Advanced.QueueDeclare(replyTo, autoDelete: true);
bus.Advanced.Consume(messageQueue, (payload, properties, info) =>
{
ReceivePdf(payload, properties, info);
return Task.FromResult(0);
});
taskInfo.InputFile = inputFile;
var html = File.ReadAllText(inputFile);
taskInfo.Html = html;
var message = PrepareMessage(new RenderRequest()
{
Html = Encoding.UTF8.GetBytes(html),
Options = new RenderRequestOptions()
{
PageSize = "A4",
ImageQuality = 70,
PageLoadRetryAttempts = 3
}
});
var correlation = Guid.NewGuid().ToString();
Console.WriteLine($"CorrelationId: {correlation}, TaskId {taskId}");
var props = new MessageProperties
{
CorrelationId = correlation,
ReplyTo = replyTo,
Expiration = "6000"
};
Publish(bus, props, message);
taskInfo.CorrelationId = Guid.Parse(correlation);
#event.WaitOne();
stopwatch.Stop();
taskInfo.TimeTaken = stopwatch.Elapsed;
return Task.FromResult(taskInfo);
}
}
catch (Exception e)
{
taskInfo.OutputFile = Empty;
return Task.FromResult(taskInfo);
}
}
void ReceivePdf(byte[] payload, MessageProperties properties, MessageReceivedInfo info)
{
var file = Format(outputFile, properties.CorrelationId);
taskInfo.OutputFile = file;
Console.WriteLine("Output written to " + file);
File.WriteAllBytes(file, payload);
var remaining = Interlocked.Decrement(ref outstandingRequests);
if (remaining == 0)
{
#event.Set();
}
}
This is a synchronous task
listOfTasks.Add(process.Process(i));
You are just adding items to the list.
This is also a synchronous task
process.Process(i);
The task that the function above returns is asynchronous and it will execute asynchronously in the whenAll call of your code.
Bear in mind that when all will wait for all tasks to run and if the task is trivial, since they start one after the other, will most times run sequentially by chance.
You will see some difference if the task code executed differentiated in execution time based on input.
First async doesn't mean multithread, async is used to run background task without blocking UI or to run I/O operations without bloking main thread.
Usually the operating system handles async with multithreading, but there is no guarantee.
If you want be sure to start multiple threads use Thread.Start.
Anyway in your code you force your code to run synchronously, because you call the async method start in the Main method without await.
You need to change the code to:
static async void Main(string[] args)
{
var t = await Start();
}
or without waiting to (but the program risk to terminate before the task complete):
static void Main(string[] args)
{
Task.Run(async () => {
var t = await Start();
});
}

Async methods wait for each other

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();
}

Refactor to remove Taks.Run from my code

I just wrote the following code
public void Save()
{
while (this.IsAsyncInProcess)
Thread.Sleep(100);
this.customer.OrderCount = this.orders.Count();
this.customer.OrderTotal = this.orders.Sum(o => x.Total);
this.customerRepo.Save();
}
public async Task LoadAsync()
{
this.IsAsyncInProcess = true;
this.customer = await this.customerRepo.GetCustomerAsync(...);
this.orders = await this.customerRepo.GetOrdersAsync(...);
this.IsAsyncInProcess = false;
}
Now I had a classical deadlock because, after this.orders completed, it would wait for the gui thread to resume in order to set this.IsAsyncInProcess to false. However, the gui thread was busy inside Save()
Now I refactored LoadAsync to
public async Task LoadAsync()
{
await Task.Run(async () =>
{
this.IsAsyncInProcess = true;
this.customer = await this.customerRepo.GetCustomerAsync(...);
this.orders = await this.customerRepo.GetOrdersAsync(...);
this.IsAsyncInProcess = false;
});
}
I can't just refactor Save to SaveAsync for compability reasons.
Is there a better way to achive this, without using Task.Run?
You can use async locking. If you can't change the signiture of Save then you can just proxy to an async local function.
static SemaphoreSlim sem = new SemaphoreSlim(1,1);
public void Save()
{
SaveAsync();
public async Task SaveAsync()
{
await sem.WaitAsync();
try{
this.customer.OrderCount = this.orders.Count();
this.customer.OrderTotal = this.orders.Sum(o => x.Total);
this.customerRepo.Save();
}finally{
sem.Release();
}
}
}
public async Task LoadAsync()
{
await sem.WaitAsync();
try{
this.customer = await this.customerRepo.GetCustomerAsync(...);
this.orders = await this.customerRepo.GetOrdersAsync(...);
}finally{
sem.Release();
}
}
or even better create your own async lock
static SemaphoreSlim sem = new SemaphoreSlim(1,1);
public static async Task<IDisposable> LockAsync(){
await sem.WaitAsync();
return Disposable.Create(()=>sem.Release());
}
public void Save()
{
SaveAsync();
public async Task SaveAsync()
{
using(await LockAsync()){
this.customer.OrderCount = this.orders.Count();
this.customer.OrderTotal = this.orders.Sum(o => x.Total);
this.customerRepo.Save();
}
}
}
public async Task LoadAsync()
{
using(await LockAsync()){
this.customer = await this.customerRepo.GetCustomerAsync(...);
this.orders = await this.customerRepo.GetOrdersAsync(...);
}
}

Task Results into a Single list

I have a method below in my WCF service:
public List<string> ProcessTask(IEnumerable<string> data)
{
var contentTasks = ..........
List<string> contentWeb = new List<string>();
Task.Factory.ContinueWhenAll(contentTasks, tasks =>
{
foreach (var task in tasks)
{
if (task.IsFaulted)
{
Trace.TraceError(task.Exception.GetBaseException().Message);
continue;
}
if (task.Result == null || String.IsNullOrEmpty(task.Result.Content))
{
continue;
}
contentWeb.Add(task.Result.Content);
}
});
}
How do I return the List of strings that have Result.Content from all
the tasks? These tasks are asynchronous tasks, so basically I have to wait until all tasks are done before I return the result.
You should return a Task<List<string>>:
public Task<List<string>> ProcessTasksAsync(IEnumerable<string> data)
{
var contentTasks = ..........
return Task.Factory.ContinueWhenAll(contentTasks, tasks =>
{
var contentWeb = new List<string>(); // Build this in the continuation
foreach (var task in tasks)
{
// ...same code...
contentWeb.Add(task.Result.Content);
}
return contentWeb; // Set the task's result here
});
}
As this is a WCF service, you can use the Task<T> method to implement an asynchronous method pair by returning the Task<T> in the Begin*** method, and unwrapping the Task<T> in the End*** method.
This makes this method asynchronous in a proper manner.
Note that this is far easier in C# 5 using async/await:
public async Task<List<string>> ProcessTasksAsync(IEnumerable<string> data)
{
var contentTasks = ..........
await Task.WhenAll(contentTasks);
var contentWeb = new List<string>(); // Build this in the continuation
foreach (var task in contentTasks)
{
// ...same code...
contentWeb.Add(task.Result.Content);
}
return contentWeb;
}
instead of continuewhenall i had to use Task.WaitAll(contentTasks);

Categories