I have some code as follows:
public void Start()
{
var watch = new Stopwatch();
watch.Start();
Task.Factory.StartNew(MyMethod1);
Task.Factory.StartNew(MyMethod2);
watch.Stop();
Log(watch.ElapsedMilliseconds);
Task.Factory.StartNew(MyMethod3);
}
Because MyMethod1 and MyMethod2 are called Asynchronously watch.Stop() gets called at the wrong time. How I can ensure that .Stop gets called and logged after MyMethod1 and MyMethod2 finish BUT ensure that MyMethod3 does not have to wait.
I want to keep all Stopwatch functionality in my Start() method and not have the logging in any of my 3 methods i.e. MyMethod1, MyMethod2 and MyMethod3
You can use the Task.Factory.ContinueWhenAll method.
watch.Start();
var t1 = Task.Factory.StartNew(MyMethod1);
var t2 = Task.Factory.StartNew(MyMethod2);
Task.Factory.ContinueWhenAll(new [] {t1, t2}, tasks => watch.Stop());
If you're targeting for .NET 4.5 and upper, you can also use the method Task.WhenAll. It returns a task that will complete when all of the passed Task objects have completed.
Task.WhenAll(t1, t2).ContinueWith(t => watch.Stop());
You need create a new Thread that will handle the logging issue.
This logging thread will wait on EventWaitHandle.WaitAll(threadsEventWaitHandles) that will contain all the threads EventWaitHandles.
Something like that :
private void LoggingThread()
{
var watch = new Stopwatch();
watch.Start();
EventWaitHandle.WaitAll(threadsEventWaitHandles);
watch.Stop();
Log(watch.ElapsedMilliseconds);
}
And also the methods MyMethod1, MyMethod2 will signal to the loging thread whene they finish.
Something like that :
private void MyMethod1()
{
//... your code
EventWaitHandle.Set();
}
private void MyMethod2()
{
//... your code
EventWaitHandle.Set();
}
So you can ensure that MyMethod3 does not have to wait.
public void Start()
{
var watch = new Stopwatch();
watch.Start();
Task.Factory.StartNew(MyMethod1);
Task.Factory.StartNew(MyMethod2);
Task.WaitAll(); // Wait for previous tasks to finish
watch.Stop();
Log(watch.ElapsedMilliseconds);
Task.Factory.StartNew(MyMethod3);
}
Related
How can I properly use the Stopwatch class with an async action event that awaits calls to my database asynchronously inside an async Post method on DotNet Core ?
Why
To time my code and check for bottleneck. This is a simplified test method that will contain more code as time goes on.
Errors
I tried using an Action event, a Task event and a Func<Task> event without success and they all give me errors which always occurs when I am awaiting a call from my database asynchronously using EF Core
When I use Action event
An unhandled exception of type 'System.ObjectDisposedException' occurred in System.Private.CoreLib.dll. Cannot access a disposed context instance.
When I use Func<Task>
System.Threading.Tasks.TaskCanceledException: A task was canceled.
It doesn't print anything when I use Task event and the rest of the code executes without errors
Code
Post Method
public async Task<JsonResult> OnPostTest() {
// my Database Context
using DatabaseContext dc = _dbContext;
// list of json data that will be returned back to the client
List<object>? listJsonData = null;
// stopwatch initialization
Stopwatch sw = new();
sw.LogActionAsync(nameof(OnPostTest), (async () => { // new Task(async () => { // new Func<Task>(async() => {
// get list of data from TableTest database with a valid name and are not marked as delete
List<TableTest> listValidTabletest = await dc.ListTest.AsNoTracking().Where(t => !string.IsNullOrWhiteSpaces(t.strName) && !t.blnDelete).ToListAsync(); //<-- returns a list asynchronously and where the error occurs
// initialize list that will be returned
listJsonData = new();
foreach (TableTest t in listValidTableTest) {
// object that will be in the list of returned json objects
var returnData = new {
t.strName,
t.arrPrices,
t.strStartDate,
t.strEndDate
};
listJsonData.Add(returnData);
}
}));
return new JsonResult(new {
// return list or an empty array if list has not been initialized
arrJsonData = listJsonData?.toArray() ?? Array.Empty<object>(),
blnGetStatus = bool.TrueString
});
}
Stopwatch Extension Class
public static async void LogActionAsync(this Stopwatch sw, string? strMethodName, Action asyncAction, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
List<Task> listOfTasks = new();
for (int i = 0; i < intNbOfIterations; i++) {
listOfTasks.Add(Task.Factory.StartNew(asyncAction)); // for Action event
//listOfTask.Add(asyncTask); // for Task event
}
await Task.WhenAll(listOfTasks);
//await asyncFuncTask; // for Func\<Task> event
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
}
EDIT:
I changed my stopwatch extension method to async Task LogActionAsync... and my stopwatch object to await sw.LogActionAsync... but now nothing is being logged*. Any idea ?
There's a lot of bugs in this code. To summarize:
async void in two places.
Missing awaits.
Using a single database context concurrently.
Adding to a list of results concurrently.
So, let's fix these one by one.
async void in two places.
Missing awaits.
As another answer noted, LogActionAsync should not be async void but should be async Task and awaited.
I changed my stopwatch extension method to async Task LogActionAsync... and my stopwatch object to await sw.LogActionAsync...
You're still missing one more async void. It's a tricky one: lambdas, when assigned to Action variables, become async void. The proper delegate type for an asynchronous method without a return value is Func<Task>, not Action.
Code:
public static async Task LogActionAsync(this Stopwatch sw, string? strMethodName, Func<Task> asyncAction, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
List<Task> listOfTasks = new();
for (int i = 0; i < intNbOfIterations; i++) {
listOfTasks.Add(asyncAction());
}
await Task.WhenAll(listOfTasks);
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
}
And now you can properly use await everywhere.
Using a single database context concurrently.
Adding to a list of results concurrently.
As another answer noted, you will need one database context per action lambda. This is a limitation of Entity Framework (in turn imposed by a limitation of most SQL on-the-wire protocols).
The List<T>.Add method is also not threadsafe, and the code is potentially invoking it from multiple threads concurrently. It's possible to use a concurrent collection, but it's easier and cleaner to return result data instead of modifying a shared collection as a side effect.
But, really, I suspect that the concurrency in the posted code is an accident. It seems very odd to run N "iterations" of something concurrently when doing timing; I believe the desired semantics are to run N iterations of something serially.
If my assumption is correct, then the code should look like this:
public static async Task LogActionAsync(this Stopwatch sw, string? strMethodName, Func<Task> asyncAction, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
for (int i = 0; i < intNbOfIterations; i++) {
await asyncAction();
}
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
}
public static async Task<T> LogActionAsync<T>(this Stopwatch sw, string? strMethodName, Func<Task<T>> asyncFunc, int intNbOfIterations = 1) {
sw.Reset();
sw.Start();
T result = default;
for (int i = 0; i < intNbOfIterations; i++) {
result = await asyncFunc();
}
sw.Stop();
// log duration to a file using Serilog
Log.Debug($"{strMethodName} Action Duration: '{sw.Elapsed.Duration()}'");
return result;
}
public async Task<JsonResult> OnPostTest() {
// my Database Context
using DatabaseContext dc = _dbContext;
// list of json data that will be returned back to the client
List<object>? listJsonData = null;
// stopwatch initialization
Stopwatch sw = new();
listJsonData = await sw.LogActionAsync(nameof(OnPostTest), (async () => {
// get list of data from TableTest database with a valid name and are not marked as delete
List<TableTest> listValidTabletest = await dc.ListTest.AsNoTracking().Where(t => !string.IsNullOrWhiteSpaces(t.strName) && !t.blnDelete).ToListAsync();
// initialize list that will be returned
var jsonData = new List<object>();
foreach (TableTest t in listValidTableTest) {
// object that will be in the list of returned json objects
var returnData = new {
t.strName,
t.arrPrices,
t.strStartDate,
t.strEndDate
};
jsonData.Add(returnData);
}
return jsonData;
}));
return new JsonResult(new {
// return list or an empty array if list has not been initialized
arrJsonData = listJsonData?.toArray() ?? Array.Empty<object>(),
blnGetStatus = bool.TrueString
});
}
You're not awaiting your call to LogActionAsync, so your call happens after your page action is over, which is why you get all those disposed exceptions. Your entire page and all its DI objects and database contexts and everything have long been disposed by that point.
async void should be considered a debugging tool, it helps find any async issue inexperienced people make right away!
The problem in your code has nothing to do with StopWatch and everything to do with entity framework.
Entity Framework DbContext is not concurrent safe.
You need to move the creation and disposal of the DbContext inside the Task.
Additionally, you should not be using Task.Factory.StartNew due to weird exception handling. And in this case, you should not use Task.Run nor Task.Factory.StartNew because you do not need a thread for concurrency.
I've a BackgroundService that execute a DoWork method.
In ExecuteAsync method, I've a list of 400 items, I measured elapsed time for all process like this:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var listItems = await _myService.GetAll();
var sw = Stopwatch.StartNew();
List<Task> l = new();
listItems.AsParallel().ForAll(x => l.Add(DoWork(x)));
await Task.WhenAll(l);
sw.Stop();
_logger.LogInformation($"{sw.Elapsed.TotalMilliseconds} ms");
}
It's works fine, globally process take 5-6 seconds.
My DoWork method (I simplified as much as possible):
private async Task DoWork(string item)
{
var sw = Stopwatch.StartNew();
var somethingResult = await otherServiceClass.DoSomething(item);
var stuffResult = await otherServiceClass.DoStuff(item);
sw.Stop();
_logger.LogInformation($"{sw.Elapsed.TotalMilliseconds} ms");
}
I would like measured each call of DoWork method, but in log the elapsed time for each call varies between 2-3 seconds.
How is it possible knowing that the overall processing takes 5-6 seconds?
If I change listItems.AsParallel().ForAll(x => l.Add(DoWork(x))); by foreach(var item in listItems) , each call of DoWork method take 50-60 ms !
Is it AsParallel.ForAll which is problematic?
How I can do this knowing that I want to user listItems.AsParallel().ForAll for process.
UPDATE
Here an example of DoSomething method. DoSomething method is declared in my application layer, and calls repository method to retrieve data from database :
public async Task<item> GetItemsIds()
{
return = await _myRepository.GetIdsItems(some parameters...);
}
Here GetIdsItems method :
public async Task<items> GetIdsItems(some parameters...)
{
return await _context.Collection.Where(condition....).FirstOrDefaultAsync().ConfigureAwait(false);
}
My backgroundservice run as Windows Service.
Here I have the following piece of code:
var tasks = new List<Task>();
var stopwatch = new Stopwatch();
for (var i = 0; i < 100; i++)
{
var person = new Person { Id = i };
list.Add(person);
}
stopwatch.Start();
foreach (var item in list)
{
var task = Task.Run(async () =>
{
await Task.Delay(1000);
Console.WriteLine("Hi");
});
tasks.Add(task);
}
await Task.WhenAll(tasks);
stopwatch.Stop();
I assume that I will have about 100 seconds in the result of the stopwatch.
But I have 1,1092223.
I think I missing something, can you help me to explain why?
I assume that your confusion might come from the await keyword in await Task.Delay(1000);
But this holds only for the innerworking of the taskmethod. Inside the loop the next iteration will be performed immidiately after Task.Run is executed. So all Tasks will be started in close succession and then run in parallel. (As far as the system has free threads at hand of course) The system takes care how, when and in which order they can be executed.
In the end in this line:
await Task.WhenAll(tasks);
you actually wait for the slowest of them (or the one started as last).
To fullfill your expectation your code should actually look like this:
public async Task RunAsPseudoParallel()
{
List<Person> list = new List<Person>();
var stopwatch = new Stopwatch();
for (var i = 0; i < 100; i++)
{
var person = new Person { Id = i };
list.Add(person);
}
stopwatch.Start();
foreach (var item in list)
{
await Task.Run(async () =>
{
await Task.Delay(1000);
Console.WriteLine("Hi");
});
}
stopwatch.Stop()
}
Disclaimer: But this code is quite nonsensical, because it uses async functionality to implement a synchronous process. In this scenario you can simply leave out the Task.Run call and use a simple Thread.Sleep(1000).
Delays are always approximate.
You are limited by when the task scheduler chooses to run the delegate you pass to Task.Run. It may be executing other tasks and be unwilling to start up more threads. Or, it may launch a new thread -- which while not slow is also not free and costs time too.
You are limited by when the task scheduler chooses to resume your code after the delay completes.
You're also limited by the OS scheduler, which may be allocating CPU time to other processes/threads and end up delaying the thread that would execute your code.
Because you are launching multiple tasks, you are seeing all of these per-task variables compound into an even larger delay.
i m new in task jobs and need to help.
I have a web service,
this service will stop all tasks and start again in every 30 mins.
Q1= Is that regular code sample ?
Q2= This code works for me, but do i need ASYNC AWAIT in this project
? i'm using .net 4.0.
Thx.
private CancellationTokenSource tokenSource;
private List<Task> Tasks;
public virtual void Start()
{
// start
Tasks = new List<Task>();
tokenSource = new CancellationTokenSource();
for (int i = 0; i < 3; i++)
{
Tasks.Add(Task.Factory.StartNew(() => SomeWork(),
tokenSource.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default));
}
}
public void Stop()
{
tokenSource.Cancel();
Task.Factory.ContinueWhenAll(Tasks.ToArray(), t =>
{
Console.WriteLine("all finished");
// start again
Start();
});
}
int i = 0;
public void SomeWork()
{
while (!tokenSource.IsCancellationRequested)
{
try
{
Thread.Sleep(1000 * 4);
Console.WriteLine(Task.CurrentId + " finised!");
}
catch (Exception) { }
}
}
Do you really need to use the async/await keywords to be able to start and stop a task? No.
Should you use the async/await keywords in your web service? Not necessarily because you don't really seem to benefit much from being able to capture the context and execute the remainder of the method once a task has finisihed. Your Start method just fires off 3 tasks and return without waiting for any of the tasks to finish. So this method isn't really "awaitable" or asynchronous by nature.
You could have made use of the async/await keywords in your Stop method if you wanted to make it asynchronous, i.e. you wanted any caller of this method be able to call it asynchronously and do something once the tasks have actually been stopped:
public async Task StopAsync()
{
tokenSource.Cancel();
await Task.WhenAll(Tasks.ToArray());
Console.WriteLine("all finished");
Start();
}
await StopAsync();
//now all tasks have been stopped...do something
When using the async and await keywords the compiler basically generates a state machine for you. The beginning of an async method is executed just like any other method and when it hits an "await" keyword it returns from the method and tells the awaitable (that is the asynchronous operation) to run the remainder of the method once it has completed. Please refer to the following link for more information about this.
How and When to use `async` and `await`
I was looking at someone sample code for async and noticed a few issues with the way it was implemented. Whilst looking at the code I wondered if it would be more efficient to loop through a list using as parallel, rather than just looping through the list normally.
As far as I can tell there is very little difference in performance, both use up every processor, and both talk around the same amount of time to completed.
This is the first way of doing it
var tasks= Client.GetClients().Select(async p => await p.Initialize());
And this is the second
var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
Am I correct in assuming there is no difference between the two?
The full program can be found below
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
RunCode1();
Console.WriteLine("Here");
Console.ReadLine();
RunCode2();
Console.WriteLine("Here");
Console.ReadLine();
}
private async static void RunCode1()
{
Stopwatch myStopWatch = new Stopwatch();
myStopWatch.Start();
var tasks= Client.GetClients().Select(async p => await p.Initialize());
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
myStopWatch.Stop();
}
private async static void RunCode2()
{
Stopwatch myStopWatch = new Stopwatch();
myStopWatch.Start();
var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
Task.WaitAll(tasks.ToArray());
Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
myStopWatch.Stop();
}
}
class Client
{
public static IEnumerable<Client> GetClients()
{
for (int i = 0; i < 100; i++)
{
yield return new Client() { Id = Guid.NewGuid() };
}
}
public Guid Id { get; set; }
//This method has to be called before you use a client
//For the sample, I don't put it on the constructor
public async Task Initialize()
{
await Task.Factory.StartNew(() =>
{
Stopwatch timer = new Stopwatch();
timer.Start();
while(timer.ElapsedMilliseconds<1000)
{}
timer.Stop();
});
Console.WriteLine("Completed: " + Id);
}
}
}
There should be very little discernible difference.
In your first case:
var tasks = Client.GetClients().Select(async p => await p.Initialize());
The executing thread will (one at a time) start executing Initialize for each element in the client list. Initialize immediately queues a method to the thread pool and returns an uncompleted Task.
In your second case:
var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
The executing thread will fork to the thread pool and (in parallel) start executing Initialize for each element in the client list. Initialize has the same behavior: it immediately queues a method to the thread pool and returns.
The two timings are nearly identical because you're only parallelizing a small amount of code: the queueing of the method to the thread pool and the return of an uncompleted Task.
If Initialize did some longer (synchronous) work before its first await, it may make sense to use AsParallel.
Remember, all async methods (and lambdas) start out being executed synchronously (see the official FAQ or my own intro post).
There's a singular major difference.
In the following code, you are taking it upon yourself to perform the partitioning. In other words, you're creating one Task object per item from the IEnumerable<T> that is returned from the call to GetClients():
var tasks= Client.GetClients().Select(async p => await p.Initialize());
In the second, the call to AsParallel is internally going to use Task instances to execute partitions of the IEnumerable<T> and you're going to have the initial Task that is returned from the lambda async p => await p.Initialize():
var tasks = Client.GetClients().AsParallel().
Select(async p => await p.Initialize());
Finally, you're not really doing anything by using async/await here. Granted, the compiler might optimize this out, but you're just waiting on a method that returns a Task and then returning a continuation that does nothing back through the lambda. That said, since the call to Initialize is already returning a Task, it's best to keep it simple and just do:
var tasks = Client.GetClients().Select(p => p.Initialize());
Which will return the sequence of Task instances for you.
To improve on the above 2 answers this is the simplest way to get an async/threaded execution that is awaitable:
var results = await Task.WhenAll(Client.GetClients()
.Select(async p => p.Initialize()));
This will ensure that it spins separate threads and that you get the results at the end. Hope that helps someone. Took me quite a while to figure this out properly since this is very not obvious and the AsParallel() function seems to be what you want but doesn't use async/await.