I have a class which has a property called ActiveDelay which is used to define a duration during which a condition evaluation should wait before checking if the condition is still true after that time. The property SetpointA is the value used to compare the given value to.
Below is what I am currently doing
public void EvaluateCondition(T value)
{
if(value.Equals(SetpointA))
{
Task.Delay(ActiveDelay).ContinueWith(_ => EvaluateConditionDelayed(GetValue())).Wait();
}
}
private void EvaluateConditionDelayed(object value)
{
if (value.Equals(SetpointA))
{
Console.WriteLine("Waited and worked");
}
else
{
Console.WriteLine("Condition now false");
}
}
I am calling the function EvaluateCondition twice, once with a TimeSpan of 5 seconds and another time with a TimeSpan of 2 seconds and I expect that the 2 second call should finish before the 5 second call but what actually happens is that the 5 second call waits and then the 2 second call waits.
I assume there is somethings to do with async and await here but I haven't found information that helps me.
Something I want to make absolutely clear, THESE DELAYS SHOULD NOT PAUSE THE PROGRAM, they should only wait externally and allow the rest of the program to run unaffected.
EXTENSIVE CODE SECTION
Here is an extensive version of the above
public class AlarmCondition
{
#region Property declaration
public TimeSpan ActiveDelay { get; set; } // Need to be historised ?
public TimeSpan ClearDelay { get; set; } // Need to be historised ?
#endregion
#region Constructor
public AlarmCondition() { }
public AlarmCondition(TimeSpan activeDelay, TimeSpan clearDelay)
{
ActiveDelay = activeDelay;
ClearDelay = clearDelay;
}
#endregion
}
public class EqualCondition<T> : AlarmCondition
{
#region Property declaration
public T SetpointA { get; }
#endregion
#region Constructor
public EqualCondition() { }
public EqualCondition(TimeSpan activeDelay, TimeSpan clearDelay) : base(activeDelay, clearDelay)
{
SetpointA = setpointA;
}
#endregion
public void EvaluateCondition(T value)
{
if(value.Equals(SetpointA))
{
Task.Delay(ActiveDelay).ContinueWith(_ => EvaluateConditionDelayed(value));
}
}
private void EvaluateConditionDelayed(T value)
{
if (value.Equals(SetpointA))
{
Console.WriteLine("Waited and worked");
}
else
{
Console.WriteLine("Condition now false");
}
}
}
// In another file and namespace
public class ConsoleDisplay
{
public static void Main(string[] args)
{
EqualCondition<bool> condition1 = new EqualCondition<bool>(new TimeSpan(0, 0, 5), new TimeSpan(0, 0, 5));
EqualCondition<bool> condition2 = new EqualCondition<bool>(new TimeSpan(0, 0, 2), new TimeSpan(0, 0, 5));
condition1.EvaluateCondition(true);
condition2.EvaluateCondition(true);
}
}
You can return Task from your method so the caller will decide if he wants to wait for the result. For example:
public async Task EvaluateCondition(T value)
{
if(value.Equals(SetpointA))
{
await Task.Delay(ActiveDelay);
EvaluateConditionDelayed(GetValue());
}
}
Or just remove .Wait() (which blocks) from your current implementation if you don't want caller to have an option for such decision. The task should still print in the console even without it (if your program runs long enough):
public void EvaluateCondition(T value)
{
if(value.Equals(SetpointA))
{
Task.Delay(ActiveDelay).ContinueWith(_ => EvaluateConditionDelayed(GetValue()));
}
}
UPD
To start multiple EvaluateCondition and wait for them to finish in parallel you can use Task.WhenAll:
var ev1Task = EvaluateCondition(someVal1);
var ev2Task = EvaluateCondition(someVal2);
await Task.WhenAll(ev1Task, ev2Task);
If you don't want to await then just skip the await Task.WhenAll(ev1Task, ev2Task) (and fix the warnings).
Related
Background:
I have an application I am developing that deals with a large number of addons for another application. One if its primary uses is to safely modify file records in files with fewer records so that they may be treated as one file (almost as if it is combing the files together into one set of records. To do this safely it keeps track of vital information about those files and changes made to them so that those changes can be undone if they don't work as expected.
When my application starts, it analyzes those files and keeps essential properties in a cache (to reduce load times). If a file is missing from the cache, the most important stuff is retrieved and then a background worker must process the file for more information. If a file that was previously modified has been updated with a new version of the file, the UI must confirm this with the user and its modification data removed. All of this information, including information on its modification is stored in the cache.
My Problem:
My problem is that neither of these processes are guaranteed to run (the confirmation window or the background file processor). If either of them run, then the cache must be updated by the main thread. I don't know enough about worker threads, and which thread runs the BackgroundWorker.RunWorkerCompleted event handler in order to effectively decide how to approach guaranteeing that the cache updater is run after either (or both) processes are completed.
To sum up: if either process is run, they both must finish and (potentially) wait for the other to be completed before running the cache update code. How can I do this?
ADJUNCT INFO (My current intervention that doesn't seem to work very well):
I have a line in the RunWorkerCompleted handler that waits until the form reference is null before continuing and exiting but maybe this was a mistake as it sometimes locks my program up.
SpinWait.SpinUntil(() => overwriteForm == null);
I haven't included any more code because I anticipate that this is more of a conceptual question than a code one. However, if necessary, I can supply code if it helps.
I think CountDownTask is what you need
using System;
using System.Threading;
public class Program
{
public class AtomicInteger
{
protected int value = 0;
public AtomicInteger(int value)
{
this.value = value;
}
public int DecrementAndGet()
{
int answer = Interlocked.Decrement(ref value);
return answer;
}
}
public interface Runnable
{
void Run();
}
public class CountDownTask
{
private AtomicInteger count;
private Runnable task;
private Object lk = new Object();
private volatile bool runnable;
private bool cancelled;
public CountDownTask(Int32 count, Runnable task)
{
this.count = new AtomicInteger(count);
this.task = task;
this.runnable = false;
this.cancelled = false;
}
public void CountDown()
{
if (count.DecrementAndGet() == 0)
{
lock (lk)
{
runnable = true;
Monitor.Pulse(lk);
}
}
}
public void Await()
{
lock (lk)
{
while (!runnable)
{
Monitor.Wait(lk);
}
if (cancelled)
{
Console.WriteLine("Sorry! I was cancelled");
}
else {
task.Run();
}
}
}
public void Cancel()
{
lock (lk)
{
runnable = true;
cancelled = true;
Monitor.Pulse(lk);
}
}
}
public class HelloWorldTask : Runnable
{
public void Run()
{
Console.WriteLine("Hello World, I'm last one");
}
}
public static void Main()
{
Thread.CurrentThread.Name = "Main";
Console.WriteLine("Current Thread: " + Thread.CurrentThread.Name);
CountDownTask countDownTask = new CountDownTask(3, new HelloWorldTask());
Thread worker1 = new Thread(() => {
Console.WriteLine("Worker 1 run");
countDownTask.CountDown();
});
Thread worker2 = new Thread(() => {
Console.WriteLine("Worker 2 run");
countDownTask.CountDown();
});
Thread lastThread = new Thread(() => countDownTask.Await());
lastThread.Start();
worker1.Start();
worker2.Start();
//countDownTask.Cancel();
Console.WriteLine("Main Thread Run");
countDownTask.CountDown();
Thread.Sleep(1000);
}
}
let me explain (but you can refer Java CountDownLatch)
1. To ensure a task must run after another tasks, we need create a Wait function to wait for they done, so I used
while(!runnable) {
Monitor.Wait(lk);
}
2. When there is a task done, we need count down, and if count down to zero (it means all of the tasks was done) we will need notify to blocked thread to wake up and process task
if(count.decrementAndGet() == 0) {
lock(lk) {
runnable = true;
Monitor.Pulse(lk);
}
}
Let read more about volatile, thanks
While dung ta van's "CountDownTask" answer isn't quite what I needed, it heavily inspired the solution below (see it for more info). Basically all I did was add some extra functionality and most importantly: made it so that each task "vote" on the outcome (true or false). Thanks dung ta van!
To be fair, dung ta van's solution DOES work to guarantee execution which as it turns out isn't quite what I needed. My solution adds the ability to make that execution conditional.
This was my solution which worked:
public enum PendingBool
{
Unknown = -1,
False,
True
}
public interface IRunnableTask
{
void Run();
}
public class AtomicInteger
{
int integer;
public int Value { get { return integer; } }
public AtomicInteger(int value) { integer = value; }
public int Decrement() { return Interlocked.Decrement(ref integer); }
public static implicit operator int(AtomicInteger ai) { return ai.integer; }
}
public class TaskElectionEventArgs
{
public bool VoteResult { get; private set; }
public TaskElectionEventArgs(bool vote) { VoteResult = vote; }
}
public delegate void VoteEventHandler(object sender, TaskElectionEventArgs e);
public class SingleVoteTask
{
private AtomicInteger votesLeft;
private IRunnableTask task;
private volatile bool runTask = false;
private object _lock = new object();
public event VoteEventHandler VoteCast;
public event VoteEventHandler TaskCompleted;
public bool IsWaiting { get { return votesLeft.Value > 0; } }
public PendingBool Result
{
get
{
if (votesLeft > 0)
return PendingBool.Unknown;
else if (runTask)
return PendingBool.True;
else
return PendingBool.False;
}
}
public SingleVoteTask(int numberOfVotes, IRunnableTask taskToRun)
{
votesLeft = new AtomicInteger(numberOfVotes);
task = taskToRun;
}
public void CastVote(bool vote)
{
votesLeft.Decrement();
runTask |= vote;
VoteCast?.Invoke(this, new TaskElectionEventArgs(vote));
if (votesLeft == 0)
lock (_lock)
{
Monitor.Pulse(_lock);
}
}
public void Await()
{
lock(_lock)
{
while (votesLeft > 0)
Monitor.Wait(_lock);
if (runTask)
task.Run();
TaskCompleted?.Invoke(this, new TaskElectionEventArgs(runTask));
}
}
}
Implementing the above solution was as simple as creating the SingleVoteTask in the UI thread and then having each thread affecting the outcome cast a vote.
I have this method:
public static async Task OpenPageAsync(string route)
{
await Shell.Current.GoToAsync(route, true);
}
If the method is called more than once in 5 seconds I would like the second call to be ignored. Has anyone come across a way to deal with this need?
Note that if it helps I do have access to create properities at the App level like this etc.
public partial class App : Application
{
public static int LastTapTime;
public static int TapTime;
In our project, we have created a 'MaxFrequencyUpdater' for exactly that cause.
Only difference: if within 5 seconds a new call comes in, it is delayed and executed after the 5 seconds interval.
namespace Utils
{
public class MaxFrequencyUpdater
{
private readonly WinformsExceptionHandler _exceptionHandler;
private readonly string _name;
private readonly int _millis;
private MethodInvoker _currentMethod;
private DateTime _lastExecuted = DateTime.MinValue;
private readonly object _updaterLockObject = new object();
public MaxFrequencyUpdater(string name, int maxFrequencyInMillis, WinformsExceptionHandler exceptionHandler)
{
_name = name;
_exceptionHandler = exceptionHandler;
_millis = maxFrequencyInMillis;
}
public void Update(MethodInvoker method)
{
lock (_updaterLockObject)
{
_currentMethod = method;
}
Task.Run(HandleWork);
}
private void HandleWork()
{
lock (_updaterLockObject)
{
// No longer bother, someone else handled it already
if (_currentMethod == null) return;
var now = DateTime.Now;
var delay = (int)(_millis - now.Subtract(_lastExecuted).TotalMilliseconds);
// Post-pone if too soon
if (delay > 0)
{
Task.Delay(delay).ContinueWith(HandleWork);
}
else
{
try
{
_currentMethod.Invoke();
}
catch (Exception e)
{
_exceptionHandler.HandleException(e);
}
_lastExecuted = now;
_currentMethod = null;
}
}
}
}
}
usage:
_maxFrequencyUpdater.Update(() =>
{
doSomething();
});
I'm trying to create my own C# task scheduler, so for example I want to run a specific void with an Id argument every Monday of the week. I also want to keep a list of all running tasks per scheduler.
So you would have a scheduler that contains a list of tasks and those tasks have actions and triggers, actions being the method(s) that I want to execute and triggers being for example every Monday of every week.
Now when the task is done and it has reached it's end date it has to pretty much dispose itself like it never existed. This is where I don't know what to do anymore.
Now this is an extreme example but I tried scheduling one million tasks that would run after 10 seconds. All the tasks ran but somehow were not disposed correctly. Visual Studio said that the Process Memory was about 700 MB and the Heap Memory about 2 MB after the tasks have disposed themselves.
I tried two things:
A flush system that runs every 30 seconds and buffers finished tasks and removes them from the list and then from the buffer. This worked kinda, after running one million tasks it would give me a "Collection was modified" exception.
Self disposing tasks, when the task is finished it will dispose of itself. When running this with one hundred thousand tasks it would dispose most of them and remove them from the list but I had at least five thousand tasks still in the task list.
My question is how do I correctly and reliably dispose the tasks and remove them from the task list so that they are no longer existing within the memory without getting any exceptions such as "Collection was modified".
Here is my code that I used, you might need to edit it a little to make it use the flush system and the self disposing system.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using static TaskScheduler.Scheduler;
namespace TaskScheduler
{
internal class Program
{
public static void Main(string[] args)
{
Scheduler scheduler = new Scheduler(new TimeSpan(0, 0, 30));
for (int i = 0; i < 100000; i++)
{
scheduler.Schedule(Function, new Settings() { Id = i, Start = DateTime.Now.AddSeconds(10) });
}
scheduler.Schedule(Function, new Settings() { Id = 1123, Recurring = true, Start = DateTime.Now.AddSeconds(5), End = DateTime.Now.AddDays(14) });
while (true)
{
Console.WriteLine(scheduler.Tasks.Count());
System.Threading.Thread.Sleep(500);
}
}
public static void Function(Task task)
{
Console.WriteLine($"Test function: {task._settings.Id}");
}
}
public class Scheduler : IDisposable
{
public List<Task> Tasks = new List<Task>();
public List<Task> FlushCollection = new List<Task>();
private Timer timer; //Flush timer
public Scheduler(TimeSpan time)
{
timer = new Timer(time.TotalMilliseconds);
timer.Elapsed += new ElapsedEventHandler(Flush);
timer.Start();
}
public void Flush(object sender, ElapsedEventArgs args)
{
foreach (Task task in Tasks.ToArray())
{
if (task.timer == null)
{
FlushCollection.Add(task);
}
}
foreach(Task task in FlushCollection.ToArray())
{
Tasks.Remove(task);
}
FlushCollection.Clear();
}
public void Schedule(Action<Task> action, Settings settings)
{
Tasks.Add(new Task(this, action, settings));
}
public void Unschedule(Task task)
{
task.Dispose();
Tasks.Remove(task);
}
public void Unschedule(int id)
{
Unschedule(Tasks.Where(x => x._settings.Id == id).FirstOrDefault());
}
public void Dispose()
{
foreach (Task task in Tasks.ToArray())
{
task.Dispose();
}
Tasks.Clear();
}
public class Task : IDisposable
{
public Scheduler _scheduler;
public Action<Task> _action;
public Settings _settings;
public Timer timer;
private DateTime next;
public Task(Scheduler scheduler, Action<Task> action, Settings settings)
{
_scheduler = scheduler;
_action = action;
_settings = settings;
Init();
}
public void Init()
{
next = DateTime.Now + _settings.Interval;
timer = new Timer((_settings.Start - DateTime.Now).TotalMilliseconds);
timer.Elapsed += new ElapsedEventHandler(Elapsed);
timer.Start();
if (_settings.Interval.TotalMilliseconds != 0)
{
timer.Interval = _settings.Interval.TotalMilliseconds;
}
}
public void Elapsed(object sender, ElapsedEventArgs args)
{
if (!Ready())
{
return;
}
Run();
}
public void Dispose()
{
timer.Dispose();
timer = null;
}
public bool Ready()
{
return DateTime.Now >= next;
}
public void Run()
{
_action(this);
if (Expired() || !_settings.Recurring)
{
_scheduler.Unschedule(this);
}
}
public bool Expired()
{
if (DateTime.Now >= _settings.End)
{
return true;
}
return false;
}
}
public class Settings
{
public int? Id { get; set; }
public bool Recurring { get; set; } = false;
public TimeSpan Interval { get; set; } //Not required when not recurring.
public DateTime Start { get; set; } = DateTime.Now;
public DateTime End { get; set; } = DateTime.Now.AddTicks(1);
}
}
}
Keep in mind this is just a prototype so it doesn't contain the whole trigger and action system yet and other things I mentioned.
I will use Quartz.NET and or Hangfire as scheduler solution.
https://www.quartz-scheduler.net
https://www.hangfire.io
Suppose I have this :
public class UploadDicomSet
{
public UploadDicomSet()
{
var cachCleanTimer = Observable.Interval(TimeSpan.FromMinutes(2));
cachCleanTimer.Subscribe(CheckUploadSetList);
//Start subscriber
}
void CheckUploadSetList(long interval)
{
//Stop and dispose subscriber
}
public void AddDicomFile(SharedLib.DicomFile dicomFile)
{
//Renew subscriber, call CheckUploadSetList 2 minutes later
}
}
1- in CheckUploadSetList I want to dispose or finish observable
2- in AddDicomFile I want to reset it
as comment in methods.
UPDATE:
I can do it by Timer as:
public class UploadDicomSet : ImportBaseSet
{
Timer _timer;
public UploadDicomSet()
{
_timer = new Timer(CheckUploadSetList, null, 120000, Timeout.Infinite);
}
void CheckUploadSetList(object state)
{
Logging logging = new Logging(LogFile);
try
{
_timer.Dispose(); //Stop the subscription
//dispose everything
}
catch (Exception exp)
{
logging.Log(ErrorCode.Error, "CheckUploadSetList() failed..., EXP:{0}", exp.ToString());
}
}
public void AddDicomFile(SharedLib.DicomFile dicomFile)
{
_timer.Change(120000, Timeout.Infinite);
}
}
Thanks in advance.
You should use Switch() for this kind of thing.
Something like this:
public class UploadDicomSet : ImportBaseSet
{
IDisposable subscription;
Subject<IObservable<long>> subject = new Subject<IObservable<long>>();
public UploadDicomSet()
{
subscription = subject.Switch().Subscribe(s => CheckUploadSetList(s));
subject.OnNext(Observable.Interval(TimeSpan.FromMinutes(2)));
}
void CheckUploadSetList(long interval)
{
subject.OnNext(Observable.Never<long>());
// Do other things
}
public void AddDicomFile(SharedLib.DicomFile dicomFile)
{
subject.OnNext(Observable.Interval(TimeSpan.FromMinutes(2)));
// Reset the subscription to go off in 2 minutes from now
// Do other things
}
}
Using Reactive Extension for just some timer function seems a bit overkill to me. Why not just use an ordinary timer for this, and start/stop it at given times?
Let me give an idea.
public class UploadDicomSet : ImportBaseSet
{
IDisposable subscription;
public void CreateSubscription()
{
var cachCleanTimer = Observable.Interval(TimeSpan.FromMinutes(2));
if(subscription != null)
subscription.Dispose();
subscription = cachCleanTimer.Subscribe(s => CheckUploadSetList(s));
}
public UploadDicomSet()
{
CreateSubscription();
// Do other things
}
void CheckUploadSetList(long interval)
{
subscription.Dispose(); // Stop the subscription
// Do other things
}
public void AddDicomFile(SharedLib.DicomFile dicomFile)
{
CreateSubscription(); // Reset the subscription to go off in 2 minutes from now
// Do other things
}
}
Background material
I really can recommend these sites:
http://www.introtorx.com/
http://rxwiki.wikidot.com/101samples
I have a search task that makes a request to an API, within a portable class library, when the user enters text in the textbox, this works as expected but I have a concern over performance at scale. When we have a large userbase all making requests to this API on every key press I can foresee performance issues.
I have limited the API call to only fire when there are more than three valid characters but I want to dampen this further. I could implement a timer over the top of this but it does not feel like a good solution and is not present in the PCL framework.
Is there a recommended pattern to achieve this type of request dampening?
private async Task GetClubs()
{
try
{
if (!string.IsNullOrWhiteSpace(ClubSearch) && ClubSearch.Replace(" ", "").Length >= 3)
{
Clubs = await dataService.GetClubs(ClubSearch);
}
}
catch (DataServiceException ex)
{
...
}
}
Usually that is done with timer. When search text changes you start (or reuse) a timer which will fire after delay and execute search request. If more text is typed during that delay - timer is reset. Sample code:
public class MyClass {
private readonly Timer _timer;
const int ThrottlePeriod = 500; // ms
public MyClass() {
_timer = new System.Threading.Timer(_ => {
ExecuteRequest();
}, null, Timeout.Infinite, Timeout.Infinite);
}
private string _searchTerm;
public string SearchTerm
{
get { return _searchTerm; }
set
{
_searchTerm = value;
ResetTimer();
}
}
private void ResetTimer() {
_timer.Change(ThrottlePeriod, Timeout.Infinite);
}
private void ExecuteRequest() {
Console.WriteLine(SearchTerm);
}
}
If timer is not available, you can do the same with Task.Delay:
public class MyClass
{
const int ThrottlePeriod = 500; // ms
private string _searchTerm;
public string SearchTerm
{
get { return _searchTerm; }
set
{
_searchTerm = value;
SearchWithDelay();
}
}
private async void SearchWithDelay() {
var before = this.SearchTerm;
await Task.Delay(ThrottlePeriod);
if (before == this.SearchTerm) {
// did not change while we were waiting
ExecuteRequest();
}
}
private void ExecuteRequest()
{
Console.WriteLine(SearchTerm);
}
}
Cheap/Fast way to implement this is a Task.Delay:
var mySearchThread = new Thread (new ThreadStart (async delegate {
while (true) {
if (!String.IsNullOrWhiteSpace(seachText) {
YourSearchMethod(seachText)
};
InvokeOnMainThread ( () => {
// Refresh your datasource on the UIthread
});
await Task.Delay (2000);
}
})).Start ();
A PCL-based solution (and amazing clean way with a great framework) is to use ReactiveUI throttling (Throttle), then you can do feats like:
// Throttle searching to every 2 seconds
this.WhenAnyValue(x => x.SearchText)
.Where(x => !String.IsNullOrWhiteSpace(x))
.Throttle(TimeSpan.FromSeconds(2))
.InvokeCommand(SearchCommand)
Ref: http://reactiveui.net
Ref: http://docs.reactiveui.net/en/user-guide/when-any/index.html