Externally timing an async method passed via param? - c#

Right now, I have the following code that times the completion time of a method and prints it out:
static void Run()
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
GetTableNames().Wait();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedTicks);
}
static async Task GetTableNames() { doesStuff(); }
However, I'll be reusing that timing code a bunch, so I'd like to wrap it inside a method so that I can do something like this:
static void Run()
{
TimeMethod(GetTableNames);
TimeMethod(GetChairNames);
TimeMethod(GetStoolNames);
// etc...
}
I've been trying a few different things (Action, Delegate, Func), but haven't succeeded in finding one that allows me to call .Wait() on the passed function before stopping the timer. Any recommendations?

Declare TimeMethod to take Func<Task>:
TimeMethod(Func<Task> method)
{
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
method().Wait();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedTicks);
}

Related

Argument value different inside called function

I am using the code below for creating multiple tasks in C#:
private static List<Task> _taskList = new List<Task>();
private static ConcurrentQueue<string> cred = new ConcurrentQueue<string>();
private static void TaskMethod1(string usercred)
{
// I am doing a bunch of operations here, all of them can be replaced with
// a sleep for 25 minutes.
// After all operations are done, enqueue again.
cred.Enqueue("usercred")
}
private static void TaskMethod()
{
while(runningService)
{
string usercred;
// This will create more than one task in parallel to run,
// and each task can take up to 30 minutes to finish.
while(cred.TryDequeue(out usercred))
{
_taskList.Add(Task.Run(() => TaskMethod1(usercred)));
}
}
}
internal static void Start()
{
runningService = true;
cred.enqueue("user1");
cred.enqueue("user2");
cred.enqueue("user3");
Task1 = Task.Run(() => TaskMethod());
}
I am encountering a strange behaviour in the code above. By putting a breakpoint at line _taskList.Add(Task.Run(() => TaskMethod1(usercred)));, I am checking value of usercred every time TaskMethod1 is called and it is not null while being called but in one of the cases the value of usercred is null inside TaskMethod1. I have no clue how this could be happening.
You are using Task.Run where in you are using variables from while loop. You are not passing it to the task. So, by the time task executes, its value gets changed.
You should use
while (runningService)
{
string usercred;
// This will create more than one task in parallel to run,
// and each task can take upto 30 minutes to finish.
while (cred.TryDequeue(out usercred))
{
_taskList.Add(Task.Factory.StartNew((data) => TaskMethod1(data.ToString()), usercred)
}
}
You should declare the usercred variable inside the inner while loop, so that the lambda inside the Task.Run captures a separate variable for each loop, and not the same variable for all loops.
while(runningService)
{
while(cred.TryDequeue(out var usercred))
{
_taskList.Add(Task.Run(() => TaskMethod1(usercred)));
}
}
As a side note, I would consider using a BlockingCollection instead of the ConcurrentQueue, to have a way to block the current thread until an item is available, so that I don't have to worry about creating inadvertently a tight loop.
The following change solved the problem.
private static void TaskMethod()
{
while(runningService)
{
string usercred;
// This will create more than one task in parallel to run,
// and each task can take up to 30 minutes to finish.
while(cred.TryDequeue(out usercred))
{
var uc = usercred;
_taskList.Add(Task.Run(() => TaskMethod1(uc)));
}
}
}

Why is my async method finishing before the next line in the caller method is executed?

Consider the following method:
public static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
// Begin timing.
stopwatch.Start();
Task task = dosomething(stopwatch);
Console.WriteLine("Waiting for async to finish {0}", stopwatch.Elapsed);
task.Wait();
Console.WriteLine("After wait {0}",stopwatch.Elapsed);
// Stop timing.
stopwatch.Stop();
// Write result.
}
public static async Task dosomething(Stopwatch stopwatch)
{
Console.WriteLine("Started async", stopwatch.Elapsed);
await dos();
Console.WriteLine("Ended async", stopwatch.Elapsed);
}
public static async Task dos()
{
Thread.Sleep(5000);
Thread.Sleep(1000);
}
}
}
I would have expected the output to be in this order:
Started Async
Waiting for async to finish
Ended async
After wait
But instead I get the following output
Started async
Ended async
Waiting for async to finish 00:00:06.0990283
After wait 00:00:06.0992659
I thought while you await the completion of a method, control returns to the caller. Hence "waiting for async to finish" should have been called before "ended async".
Thread.Sleep is not asynchronous. Therefore, the thread will be blocked and your code will be executed synchronously. If you exchange your Thread.Sleep with await Task.Delay, you'll get your expected output.
For more information about the warning that the compiler should have emitted (CS1998), see this question. Quoting Stephen Cleary:
An incredibly common async-newbie mistake is to assume async means "make this method asynchronous". This is commonly paired with the assumption that "asynchronous" means "run on a background thread", but sometimes it's just an assumption of "magic".
Thus, the warning explicitly points out that the code will run
synchronously.
As mentioned in other answers Task.Delay() works but also, you do not need to create a task object reference and then call it. I just called .Wait() on doSomething and it worked for me.
Here is my solution
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
// Begin timing.
stopwatch.Start();
Console.WriteLine("Waiting for async to finish {0}", stopwatch.Elapsed);
dosomething(stopwatch).Wait();
Console.WriteLine("After wait {0}", stopwatch.Elapsed);
// Stop timing.
stopwatch.Stop();
// Write result.
Console.ReadLine();
}
public static async Task dosomething(Stopwatch stopwatch)
{
Console.WriteLine("Started async", stopwatch.Elapsed);
await Task.Delay(6000);
Console.WriteLine("Ended async", stopwatch.Elapsed);
}

Time measurement of functions with different signature in C#

I want to measure the time that certain function calls take in my application. For this I use the Stopwatch class and it works fine. It looks something like this:
static readonly Stopwatch StopWatch = new Stopwatch();
StopWatch.Restart();
void func();
StopWatch.Stop();
Assert.Blabla
However I am typing this around a lot of functions. Is there a way to make a function that does this for me? I tried but since the signatures of the functions are all different I can't figure it out. I took a look at Func and Action, but they seem to require a fixed signature. I would like something like this:
CallAndMeasureFunction(func)
You can use something like below:
Define a method which takes your actual methods delegate as input:
public static TimeSpan GetTimestampFor(Action action)
{
TimeSpan timestamp = new TimeSpan(0);
Stopwatch stopWatch = new Stopwatch();
if (action != null)
{
stopWatch.Start();
action.Invoke();
stopWatch.Stop();
timestamp = stopWatch.Elapsed;
}
return timestamp;
}
and call it as below:
var timeSpan = GetTimestampFor(() => {var xyz = ActualMethodForWhichTimeHasTobeMeasured()});
With this code, you can measure every method's execution time

C# Dispatching code to run at a later time?

How can I dispatch code to run at a later time? something like :
ThreadPool.QueueUserWorkItem(callback, TimeSpan.FromSeconds(1)); // call callback() roughly one second from now
You can try the following:
System.Threading.Timer _timeoutTimer;
//...
int timeout = (int)TimeSpan.FromSeconds(1).TotalMilliseconds;
_timeoutTimer = new System.Threading.Timer(OnTimerElapsed,
null, timeout, System.Threading.Timeout.Infinite);
//...
void OnTimerElapsed(object state) {
// do something
_timeoutTimer.Dispose();
}
You can use the Timer class for this.
Just put a sleep in your callback function. If you are using Threadpool or Task it may take longer than the actual timespan you send before getting started; this is because the thread won't start executing immediately if it's queued.
public static void MyCallback(object delay)
{
Thread.Sleep(((TimeSpan)delay).TotalMilliseconds);
... code ...
}
You could do the above inline with an anonymous method and using the lower level thread construct.
new Thread(() => {
Thread.Sleep(delayMilliseconds);
callbackFunction();
}).Start();

Get time process takes to complete in seconds?

My program runs a batch file in cmd.exe, after it finished I want to display a MessageBox to user saying Finished in #.## seconds,
I'm redirecting CMD output to a textbox using process.BeginOutputReadLine(), this is the code I tried:
if (e.Data == null)
{
string time = process.TotalProcessorTime.Seconds.ToString();
MessageBox.Show("Finished in " + time + " seconds");
}
It took about 7-15 seconds to complete the process, but the MessageBox displayed Finished in 0 seconds.
How do I get the accurate time it took to complete in seconds?
Stopwatch watch = new Stopwatch();
watch.Start();
//Do things
watch.Stop();
Text = watch.Elapsed.TotalSeconds.ToString();
Have you tried process.ExitTime.Subtract(process.StartTime).TotalSeconds?
Edited to add: Note that you will get an exception if you try to use this before the process has exited. Per the documentation for ExitTime, use the HasExited property if you have any doubt as to whether this is the case or not.
Could you ultimately do something like this if it makes it easier to read for you
var procTime = DateTime.Now - Process.GetCurrentProcess().StartTime;
var procTimeInSec = procTime.Seconds;
MessageBox.Show(string.Format("Finished in {0} seconds", procTimeInSec));
to access you may want to change the two `var local variables to be accessible from outside the local scope.
below is how you would declare it outside of the local method at the top of the Class
public static TimeSpan procTime = new TimeSpan();
var procTimeInSec, can still be declared in the local scope
Just basic Stopwatch should be enough.
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
// run external process, if asynchronous -
//store stopWatch in member variable instead of local
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Note that TotalProcessorTime measures CPU usage time, which could very well be 0 seconds for CMD.exe as it really does not do much.
You should look into the Stopwatch class. You'll need to start the stopwatch before your process begins, then stop it afterwards and get the elapsed time.
You should use the Stopwatch class when starting the process.
On the process, add an event handler for the Exited event and when the process is done, stop the stopwatch and retrieve the time.
class Foo
{
Stopwatch watch = new Stopwatch();
public void RunProcess(Process process)
{
process.Exited += new EventHandler(ProcessExit);
process.Start();
watch.Start();
}
public void ProcessExit(object sender, EventArgs e)
{
watch.Stop();
}
}

Categories

Resources