Using Task.Delay and a recursive call - c#

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

Related

Windows service doesn't wait for running tasks to get finished

I am writing a windows service which creates a couple of parallels tasks to run:
Following is the sample code snippet:
private static void TaskMethod1()
{
//I am doing a bunch of operations here, all of them can be replaced with a sleep for 25 minutes
}
private static async Task TaskMethod()
{
while(runningService)
{
// Thi will create more than one task in parallel to run and each task can take upto 30 minutes to finish
Task.Run(() => TaskMethod1(arg1);
}
}
internal static void Start()
{
runningService = true;
Task1 = Task.Run(() => TaskMethod());
}
internal static void Stop()
{
runningService = false;
Task1.Wait();
}
Now when I stop the service, it will not create any new tasks because runningService = false but windows service doesn't wait for 30 minutes for already running tasks to get finished.
Now I read that there is an x minutes timeout for service and it can be changed using registry settings, I was just wondering if there is the way such that the service will wait for each task to be finished instead of hardcoding that time via the registry.
In my .net core services i use the interface IHostApplicationLifetime to intercept when my service is beign closed, by registering an action to call using IHostApplicationLifetime.ApplicationStopped.Register(Action callback) .
Then in the callback you could wait for the task to complete.

webapi: schedule action at certain time

In my webapi application, I want to schedule an action at certain time. Here is my code:
private readonly ConcurrentDictionary<string, Timer> timers;
public void Schedule(TimeSpan when, Action<ElapsedEventArgs, Item> expiredCallback, Item item)
{
Timer timer = null;
if (timers.TryGetValue(item.Label, out timer))
{
return;
}
timer = new Timer();
timer.Interval = when.TotalMilliseconds;
var addResult = this.timers.TryAdd(item.Label, timer);
if (addResult)
{
timer.Elapsed += (sender, e) =>
{
Timer expiredTimer = null;
if (this.timers.TryRemove(item.Label, out expiredTimer))
{
expiredTimer.Enabled = false;
expiredTimer.Dispose();
}
expiredCallback(e, item);
};
timer.Start();
}
}
The problem with this code is that if the application pool recycles after I schedule an action, I am assuming that the action will not be executed, since the timers are held in memory.
A better solution is to schedule a task using a scheduler api and from that scheduled task to call the api, but this would complicate things... So is there a simple way to make this code work in the scenario that I've described?
Postgres, have pgAgent to control shedules. It is a free easy_to_use time sheduler, where you may shedule all tasks that needs to be run. All the tasks are saved in tables, so nothing is lost if the webserver restarts. And it logs in the tablelog, if a shedule fails.
Example picks of the pgagent (my local server).

Cancelling async method that calls events

I have a headless UWP application that uses an external library to connect to a serial device and send some commands. It runs an infinite loop (while true) with a 10 minute pause between loops. The measurement process takes around 4 minutes.
The external library needs to run 3 measurements and after each it signals by raising an event. When the event is raised the 4th time I know that I can return the results.
After 4 hours (+/- a few seconds) the library stops raising events (usually it raises the event one or 2 times and then it halts, no errors, nothing).
I implemented in DoMeasureAsync() below a CancellationTokenSource that was supposed to set the IsCancelled property on the TaskCompletionSource after 8 minutes so that the task returns and the loop continues.
Problem:
When the measurement does not complete (the NMeasureCompletionSource never gets its result set in class CMeasure), the task from nMeasureCompletionSource is never cancelled. The delegate defined in RespondToCancellationAsync() should run after the 8 minutes.
If the measurement runs ok, I can see in the logs that the code in the
taskAtHand.ContinueWith((x) =>
{
Logger.LogDebug("Disposing CancellationTokenSource...");
cancellationTokenSource.Dispose();
});
gets called.
Edit:
Is it possible that the GC comes in after the 4 hours and maybe deallocates some variables and doing so makes the app to not be able to send the commands to the sensor? - It is not the case
What am I missing here?
//this gets called in a while (true) loop
public Task<PMeasurement> DoMeasureAsync()
{
nMeasureCompletionSource = new TaskCompletionSource<PMeasurement>();
cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(8));
var t = cMeasure.Run(nitrateMeasureCompletionSource, cancellationTokenSource.Token);
var taskAtHand = nitrateMeasureCompletionSource.Task;
taskAtHand.ContinueWith((x) =>
{
Logger.LogDebug("Disposing CancellationTokenSource...");
cancellationTokenSource.Dispose();
});
return taskAtHand;
}
public class CMeasure
{
public async Task Run(TaskCompletionSource<PMeasurement> tcs, CancellationToken cancellationToken)
{
try
{
NMeasureCompletionSource = tcs;
CancellationToken = cancellationToken;
CancellationToken.Register(async () => await RespondToCancellationAsync(), useSynchronizationContext: false);
CloseDevice(); //Closing device if for some reason is still open
await Task.Delay(2500);
TheDevice = await GetDevice();
measurementsdone = 0;
Process(); //start the first measurement
}
catch (Exception ex)
{
DisconnectCommManagerAndCloseDevice();
NMeasureCompletionSource.SetException(ex);
}
}
public async Task RespondToCancellationAsync()
{
if (!NitrateMeasureCompletionSource.Task.IsCompleted)
{
Logger.LogDebug("Measure Completion Source is not completed. Cancelling...");
NMeasureCompletionSource.SetCanceled();
}
DisconnectCommManagerAndCloseDevice();
await Task.Delay(2500);
}
private void Process()
{
if (measurementsdone < 3)
{
var message = Comm.Measure(m); //start a new measurement on the device
}
else
{
...
NMeasureCompletionSource.SetResult(result);
}
}
//the method called when the event is raised by the external library
private void Comm_EndMeasurement(object sender, EventArgs e)
{
measurementsdone++;
Process();
}
}
After more testing I have reached the conclusion that there is no memory leak and that all the objects are disposed. The cancellation works well also.
So far it appears that my problem comes from the execution of the headless app on the Raspberry Pi. Although I am using the deferral = taskInstance.GetDeferral(); it seems that the execution is stopped at some point...
I will test more and come back with the results (possibly in a new post, but I will put a link here as well).
Edit:
Here is the new post: UWP - Headless app stops after 3 or 4 hours
Edit 2:
The problem was from a 3rd party library that I had to use and it had to be called differently from a headless app. Internally it was creating its own TaskScheduler if SynchronizationContext.Current was null.

Proper way to implement multiple functions in windows service

I have a windows service which performs multiple task which i have separated into functions, some will take lets say 5 minutes to complete, while some will take less.
private System.Timers.Timer tim = null;
protected override void OnStart(string[] args)
{
tim = new System.Timers.Timer();
this.tim.Interval = 30000;
this.tim.Elapsed += new System.Timers.ElapsedEventHandler(this.OnTimedEvent_Tick);
tim.Enabled = true;
}
private void OnTimedEvent_Tick(Object source, System.Timers.ElapsedEventArgs e)
{
Task task0 = Task.Factory.StartNew(() => Function1()); // doing some database operations
Task task1 = Task.Factory.StartNew(() => Function2()); // doing some other database operation
Task task10 ......Up to Function10()
Task.WaitAll(task0,task1, task2, task3, task4,task5, task6,task7,task8,task9,task10);
}
Is there a draw back to the above method? if my windows service is to run lets say every 30 seconds. IF there is how do i approach it?
This would work fine in your case since there are only limited number of tasks. For cases where number of tasks that can be created are unknown, do consider using Parallel.ForEach instead. The thing that you need to handle here is exception. Put your code in a try.... catch statement.
try
{
....your code here.....
}
catch (AggregateException e)
{
var x = e.Flatten();
// Log or do what-ever
}
The correct answer would depend on what those tasks are actually doing. If all tasks must be completed prior to restarting any of them, set tim.AutoReset = false. Then after Task.WaitAll() call tim.Start(). This will ensure your wait time is between complete executions of all tasks. Otherwise, if your timer time is smaller than task execution time, you won't see any wait time.
If some of your functions will periodically take longer than timer interval (30 seconds) it will cause threads count to increase without any control. So you will end by using all possible threads which will result in processing delays. If timer interval is shorter than processing time, consider applying pause-resume timer system

Sending many parrallel WebRequests

I'm trying to simulate many concurrent users (>2000) to test a web service. Every user performs actions at a specific pre-defined time, for example:
User A: 09:10:02, 09:10:03, 09:10:08
User B: 09:10:03, 09:10:05, 09:10:07
User C: 09:10:03, 09:10:09, 09:10:15, 09:10:20
I now want to send web request in real time at each of those times. I can tolerate a delay of at most ~2 seconds. What I was already trying without success:
a) Aggregate all times in a single list, sort it by time then iterate it:
foreach (DateTime sendTime in times) {
while (DateTime.now < sendTime)
Thread.Sleep(1);
SendRequest();
}
b) Create a thread for each user, with each thread checking for the same condition as above but a longer sleep time
Both approaches kind of work, but the delay between the time that the request was supposed to be sent and the time that it has actually been sent is way too high. Is there any way to send the requests with higher precision?
Edit: The suggests approaches work really well. However, the delay is still extremely high for many requests. Apparantly, the reason for this is my SendRequest() method:
private static async Task SendRequest()
{
// Log time difference
string url = "http://www.request.url/newaction";
WebRequest webRequest = WebRequest.Create(url);
try
{
WebResponse webResponse = await webRequest.GetResponseAsync();
}
catch (Exception e) { }
}
Note that my web service does not return any response, maybe this is the reason for the slow down? Can I send the request without waiting for the response?
Why are you doing this with multiple thread? Threading requires slow sleep/wake context switching. You could just do this all with timers/async calls.
List<DateTime> scheduledTimes = ...;
List<Task> requests = scheduledTimes
.Select(t => t - DateTime.Now)
.Select(async delay =>
{
await Task.Delay(delay);
SendRequest();
})
.ToList();
await Task.WhenAll(requests);
The above code will in one thread schedule all the requests onto the SynchronizationContext and run them.
Simples.
I would suggest to use a timer object to trigger the requests:
// In Form_Load or another init method
Timer tRequest = new Timer();
tRequest.Interval = 500;
tRequest.Tick += TRequest_Tick;
private void TRequest_Tick(object sender, EventArgs e)
{
var sendTimes = times.Where(t => t.AddMilliseconds(-500) < DateTime.Now && t.AddMilliseconds(500) > DateTime.Now);
foreach(DateTime sendTime in sendTimes)
{
SendRequest();
}
}

Categories