I am building a windows store app using C# and xaml. I need to refresh the data after certain interval of time (bring the new data from the server). I used ThreadPoolTimer to execute my refresh function periodically as follows:
TimeSpan period = TimeSpan.FromMinutes(15);
ThreadPoolTimer PeriodicTimer = ThreadPoolTimer.CreatePeriodicTimer(async(source)=> {
n++;
Debug.WriteLine("hello" + n);
await dp.RefreshAsync(); //Function to refresh the data
await Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
bv.Text = "timer thread" + n;
});
}, period);
This is working properly. The only problem is that what if refresh function doesnot complete before its next instance is submitted to the thread pool. Is there some way to specify the gap between its execution.
Step 1 : Refresh function executes (takes any amount of time)
Step 2 : Refresh function completes its execution
Step 3 : Gap for 15mins then go to Step 1
Refresh function executes. 15mins after its execution ends, it executes again.
The AutoResetEvent will solve this problem. Declare a class-level AutoResetEvent instance.
AutoResetEvent _refreshWaiter = new AutoResetEvent(true);
Then inside your code: 1. wait on it till it is signaled, and 2. pass its reference as an argument to RefreshAsync method.
TimeSpan period = TimeSpan.FromMinutes(15);
ThreadPoolTimer PeriodicTimer = ThreadPoolTimer.CreatePeriodicTimer(async(source)=> {
// 1. wait till signaled. execution will block here till _refreshWaiter.Set() is called.
_refreshWaiter.WaitOne();
n++;
Debug.WriteLine("hello" + n);
// 2. pass _refreshWaiter reference as an argument
await dp.RefreshAsync(_refreshWaiter); //Function to refresh the data
await Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
bv.Text = "timer thread" + n;
});
}, period);
Finally, at the end of dp.RefreshAsync method, call _refreshWaiter.Set(); so that if 15 seconds have passed then the next RefreshAsync may be called. Note that if RefreshAsync method takes less than 15 minutes, the execution proceeds as normal.
I think an easier way to do this is with async:
private async Task PeriodicallyRefreshDataAsync(TimeSpan period)
{
while (true)
{
n++;
Debug.WriteLine("hello" + n);
await dp.RefreshAsync(); //Function to refresh the data
bv.Text = "timer thread" + n;
await Task.Delay(period);
}
}
TimeSpan period = TimeSpan.FromMinutes(15);
Task refreshTask = PeriodicallyRefreshDataAsync(period);
This solution also provides a Task which can be used to detect errors.
Related
I have below code :
public async Task StartAsync(CancellationToken cancellationToken)
{
var cronExpressionVal = new Timer(async e => await GetCronExpression(cancellationToken), null, TimeSpan.Zero, new TimeSpan(0, 5, 0));
}
What I am trying to achieve is, GetCronExpression method should run at every 5 minutes.
But my problem is, when we first time run programme so it is coming in StartAsync method.
And it execute successfully.
Now it is not coming again in this method so my GetCronExpression method is not calling at every 5 minutes.
So my question is where should I put this GetCronExpression method call so it execute at every 5 minutes.
What I am trying to achieve is, GetCronExpression method should run at
every 5 minutes.
Well, couple of ways to handle this. However, the most efficient and easiest way to meet your requirement is to use PeriodicTimer. Importantly, you don't need to think where should you keep this. You can call your method every 5 minutes time interval from everywhere. It could be within middleware or any custom class , repository, even from controller.
Implementation Using Middleware:
public class AutoTimerMiddleware
{
private readonly RequestDelegate _next;
public AutoTimerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
int counter = 0;
while (await timer.WaitForNextTickAsync())
{
counter++;
// if (counter > 5) break;
CallThisMethodEvery5Second(counter);
}
// Move forward into the pipeline
await _next(httpContext);
}
public void CallThisMethodEvery5Second(int counter)
{
Console.WriteLine("Current counter: {0} Last Fired At: {1}", counter, DateTime.Now);
}
}
Note: When use in middleware, please register in program.cs file as following
app.UseMiddleware<AutoTimerMiddleware>();
Output:
Implementation Using Any Custom Class/Anywhere:
var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
int counter = 0;
while (await timer.WaitForNextTickAsync())
{
counter++;
if (counter > 5) break;
CallThisMethodEvery5Second(counter);
}
Note: You will call your method GetCronExpression within the WaitForNextTickAsync so that will be called at your given time frame. For the demo, I am calling this in every 5 seconds.
Method To Call:
public void CallThisMethodEvery5Second(int counter)
{
Console.WriteLine("Current counter: {0} Last Fired At: {1}",counter, DateTime.Now);
}
Output:
You have to keep reference to timer otherwise it will be garbage collected:
As long as you are using a Timer, you must keep a reference to it. As with any managed object, a Timer is subject to garbage collection when there are no references to it. The fact that a Timer is still active does not prevent it from being collected.
https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer?view=net-7.0#remarks
If it's not the case, I suggest including more context.
When calling Task.Run(..)..Wait(...) in a static constructor it waits for the entire timeout time even though the task has finished. Just curious why this is? What is best practice for this scenario? Test class that shows it below
static TestClass()
{
var delay = 100;
var wait = 500;
// Will wait the whole wait time
var sw = Stopwatch.StartNew();
Task.Run(() => DoStuff(delay)).Wait(wait);
sw.Stop();
var elapsedMs = sw.ElapsedMilliseconds;
Debug.WriteLine($"Elapsed {elapsedMs}");
// Will return when task is complete
sw.Restart();
Task.Run(()=>
{
var awaiter = Task.Run(() => DoStuff(delay)).GetAwaiter();
var maxMs = delay * TimeSpan.TicksPerMillisecond;
var swElapse = Stopwatch.StartNew();
while (!awaiter.IsCompleted && swElapse.ElapsedTicks < maxMs)
{ }
swElapse.Stop();
}).Wait(wait);
sw.Stop();
elapsedMs = sw.ElapsedMilliseconds;
Debug.WriteLine($"Elapsed {elapsedMs}");
}
static void DoStuff(int delay)
{
// Some async task called and waited for result
Task.Run(async () => await Task.Delay(100)).GetAwaiter().GetResult();
}
}
in a static constructor
Last time I checked, static constructors take a lock because only one of them can execute at a time. So, if the static constructor queues work to another thread that then does other things (i.e., call other static constructors) while the original static constructor is blocked on that other thread, then you can end up with a deadlock that is only resolved with the timeout.
What is best practice for this scenario?
Don't block on async code, especially not in constructors, and especially especially not in static constructors. (Link is to my blog).
var delay = 100;
var maxMs = delay * TimeSpan.TicksPerMillisecond;
var secondsTotal = maxMs / 1000.0m;
Console.WriteLine("Total seconds thread will wait: " + secondsTotal + "s.");
When you run this code, you get: 1000s. 1000 seconds is 16.7 minutes.
Your while condition is:
!awaiter.IsCompleted && swElapse.ElapsedTicks < maxMs
Because this is the "AND" operation, the while loop will continue so long as what's in the parentheses is true.
In your case the "awaiter.IsCompleted" evaluates for true, AND less than 16 minutes evaluates for true as well (until 16 minutes pass). Only until one of the conditions is false will the while loop stop repeating.
This is likely why you are experiencing this behavior.
This question already has answers here:
Timeout pattern on task-based asynchronous method in C#
(2 answers)
Safely stop long running task
(2 answers)
Closed 2 years ago.
I have a piece of code which does some processing of data, written in c#.
string status;
log.Info("Starting the Process");
StartProcessing(); // takes 10-12 mins usually
var task = Task.Run(() =>
{
do
{
status = GetStatus();
log.Info("Status inside loop : " + status);
} while (status != "Complete")
});
if (task.Wait(TimeSpan.FromMinutes(Convert.ToDouble(15))))
{
log.Info("Processing finished");
}
else
{
log.Info("Process did not complete in 15 mins");
log.Info("Stopping the Process");
StopProcessing(); // this takes 2-3 mins usually.
log.Info("Process stopped");
}
StartProcessing() method actually start some background processing of the data. It doesn't return any value or wait for the method to finish. So we added a do-while loop to check the status of the processing. If the status is complete , then come of the loop and proceed further.
Now the requirement has changed to put a timeout for the processing. If the processing is taking more than 5 mins, then we have to stop the process. So I have wrapped my code in Task.Run as shown above and written a if else condition to check the time.
This doesn't seem to work as expected, because when I run my code, this is log information I'm getting.
Starting the Process
Status inside loop : Processing
Status inside loop : Processing
Status inside loop : Processing
Status inside loop : Processing --> this line is repeated multiple times with in 15 mins.
Process did not complete in 15 mins.
Stopping the Process
Status inside loop : Processing
Status inside loop : Processing
Status inside loop : Processing --> why is it going back to do while after coming out ?
Process stopped
The execution is going back to do while even after coming out. Is there anything wrong am I doing here?
Any suggestions are very helpful.
You need to add code that terminates your background task after the given time. For this, you best introduce a CancellationToken to your processing task. Do something like:
CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromMinutes(5));
StartProcessing(source.Token);
and then, within your processing task, regularly call token.IsCancellationRequested and abort if this is true.
If you have access to source code of StartProcess, then like #PMF said, passing CancellationToken to the method is the efficient way.
Otherwise, you can create your own cancelable task using TaskCompletionSource and CancellationTokenSource:
Task<bool> ProcessAsync(TimeSpan timeout)
{
var tcs = new TaskCompletionSource<bool>();
var cts = new CancellationTokenSource();
// If task canceled, set to return false
cts.Token.Register(() => tcs.TrySetResult(false));
// Cancel after timeout
cts.CancelAfter(timeout);
// Start process.
StartProcess();
// Start waiting until complete or canceled.
Task.Run(() =>
{
while (GetStatus() != "Complete" && !cts.IsCancellationRequested)
{
}
if (cts.IsCancellationRequested)
{
// Task has been canceled due to timeout.
tcs.TrySetResult(false);
}
else
{
// Task done. Status is "Completed".
tcs.TrySetResult(true);
}
});
return tcs.Task;
}
Then await ProcessAsync:
// Or ProcessAsync(TimeSpan.FromMinutes(5)).Result for sync method
var isCompleted = await ProcessAsync(TimeSpan.FromMinutes(5));
if (!isCompleted)
{
StopProcess();
}
I'm trying to figure out the best way to implement a delay into Task such that after the delay it calls itself again to attempt the same work.
My application is a server that generates reports from the database after the mobile devices sync their data with the server, however If another user has called the report generation method recently, I want it to pause for a period of time and then attempt to run again.
This is my current attempt
private static DateTime _lastRequest = Datetime.MinValue;
public async void IssueReports()
{
await Task.Run(() =>
{
if (DateTime.Now < _lastRequest + TimeSpan.FromMinutes(3)) //checks to see when a user last completed this method
{
Task.Delay(TimeSpan.FromMinutes(2));
IssueReports(); //calls itself again after the delay
return;
}
});
//code to generate reports goes here
_lastRequest = DateTime.Now; //updates last request into the static variable after it has finished running
}
Initially if it failed the check then the task would just end. This prevented 2 users hitting the database at the same time and it causing duplicate reports to be generated. However, the problem is that if 2 users sync within that same window then the second user reports wouldn't be sent until another sync call is done.
The delay is supposed to give the server time to finish generating the reports and updating the database before the next batch is requested by calling itself.
Am I overcomplicating things? I'm worried about it potentially hammering system resources with multiple loops in the event the reports take a long time to process
Following example run background service every 10 seconds recursively. This method is recommended only if you believe your task will complete within 10 seconds.
public frm_testform()
{
InitializeComponent();
dispatcherTimer_Tick().DoNotAwait();
}
private async Task dispatcherTimer_Tick()
{
DispatcherTimer timer = new DispatcherTimer();
TaskCompletionSource<bool> tcs = null;
EventHandler tickHandler = (s, e) => tcs.TrySetResult(true);
timer.Interval = TimeSpan.FromSeconds(10);
timer.Tick += tickHandler;
timer.Start();
while (true)
{
tcs = new TaskCompletionSource<bool>();
await Task.Run(() =>
{
// Run your background service and UI update here
await tcs.Task;
}
}
This is what AsyncMethods class looks like:
public class AsyncMethods
{
public static async Task<double> GetdoubleAsync()
{
Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1000);
return 80d;
}
public static async Task<string> GetStringAsync()
{
Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1000);
return "async";
}
public static async Task<DateTime> GetDateTimeAsync()
{
Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(1000);
return DateTime.Now;
}
}
This what my main method looks like:
static void Main(string[] args)
{
while (Console.ReadLine() != "exit")
{
Console.WriteLine("Thread.CurrentThread.ManagedThreadId: " + Thread.CurrentThread.ManagedThreadId);
DateTime dt = DateTime.Now;
var res = GetStuffAsync().Result;
var ts = DateTime.Now - dt;
Console.WriteLine(res);
Console.WriteLine("Seconds taken: " + ts.Seconds + " milliseconds taken: " + ts.Milliseconds);
}
Console.ReadLine();
return;
}
static async Task<object> GetStuffAsync()
{
var doubleTask = AsyncMethods.GetdoubleAsync();
var StringTask = AsyncMethods.GetStringAsync();
var DateTimeTask = AsyncMethods.GetDateTimeAsync();
return new
{
_double = await doubleTask,
_String = await StringTask,
_DateTime = await DateTimeTask,
};
}
As it can be seen in each method i added a delay of 1 second. Here is the output:
Thread.CurrentThread.ManagedThreadId: 10
Thread.CurrentThread.ManagedThreadId: 10
Thread.CurrentThread.ManagedThreadId: 10
Thread.CurrentThread.ManagedThreadId: 10
{ _double = 80, _String = async, _DateTime = 2/15/2017 4:32:00 AM }
Seconds taken: 1 milliseconds taken: 40
Thread.CurrentThread.ManagedThreadId: 10
Thread.CurrentThread.ManagedThreadId: 10
Thread.CurrentThread.ManagedThreadId: 10
Thread.CurrentThread.ManagedThreadId: 10
{ _double = 80, _String = async, _DateTime = 2/15/2017 4:32:03 AM }
Seconds taken: 1 milliseconds taken: 16
Now i have 2 questions:
How come everything happened on a single thread?
Why was the Delay only 1 second when i waited 3 seconds?
First off: if you have two questions please ask two questions. Don't put two questions in one question.
How come everything happened on a single thread?
That's the wrong question to ask. The correct question is: why do you think anything should happen on a second thread?
Here, I'll give you a task: wait five minutes, and then check your email. While you're waiting, make a sandwich. Did you have to hire someone to either do the waiting or make the sandwich? Obviously not. Threads are workers. There's no need to hire a worker if the job can be done by one worker.
The whole point of await is to avoid going to extra threads if you don't need to. In this case you don't need to.
Why was the Delay only 1 second when i waited 3 seconds?
Compare these two workflows.
Wait five minutes; while you're waiting, make a sandwich
then check your email
then wait five minutes; while you're waiting, make a sandwich
then check your email
then wait five minutes; while you're waiting, make a sandwich
then check your email
If you execute that workflow, you'll wait a total of fifteen minutes.
The workflow you wrote was:
Wait five minutes
simultaneously, wait five minutes
simultaneously, wait five minutes
while you're waiting, make a sandwich
then check your email
You only wait five minutes with that workflow; all the delays happen at the same time.
Do you see how you wrote your program incorrectly now?
The key insight to understand here is that an await is a point in a program where the continuation of the await is delayed until after the awaited task completes.
If you don't put in an await, the program continues by itself without waiting. That's the meaning of await.
They all start on the same thread. When you call your three Async methods in sequence, they all execute synchronously up until the first await call. (After the await, they become state machines that pick up where they left off whenever they get scheduled. If you checked the thread ID after the await Task.Delay call, you would probably find that the continuations ran on different threads -- at least here in a console app.)
As for why it's only delaying 1 second... that's what you're telling it to do. You've got three async tasks, all running simultaneously, each delaying for one second. You're not saying "[a]wait until the first task is done before starting the second" -- in fact you're carefully doing the opposite, starting all three and then awaiting all three -- so they run in parallel.
Your Console.WriteLine() calls in GetdoubleAsync(), GetStringAsync(), and GetDateTimeAsync() happened in the calling thread because they happened before the first continuation.
Your await Task.Delay() calls yielded the thread back to the calling code.
When the task returned by Task.Delay() completed, the continuation on those Tasks returned their values and set their tasks as completed.
This allowed your 3 awaits (in sequential, synchronous order) in GetStuffAsync() to return. Each one had to wait 1 second before marked as completed, but they were yielding and happening at the same time.
I think you are looking for System.Threading.Tasks.Parallel to do things at the same time. Async...await is useful for yielding threads.
You're starting all your tasks at the same time so they're all going to run in parallel, not in sequence. That's why everything completes after 1000 milliseconds.
Additionally, async doesn't create new threads, it uses the current thread asynchronously. You can see this kind of behaviour in async javascript (which is a single threaded environment) or coroutines in Unity3D. They both allow async behaviour without threads.
So each of your tasks is being run on the same thread and completes in 1 second.