UWP ThreadPoolTimer wait until work item finished - c#

I need to implement a periodic timer (10 seconds), that will check for some changes and update Database and UI. I need to wait until "Job" is finished and then start timer again. I call
timer.Cancel()
on "Job" starting and then create timer again on finish. But seems when I use work-item handlers that use the async keyword ,the thread pool work item may be set to the complete state before all of the code in the handler has executed.
See full code :
private bool IsStop = false;
private async Task DoWork()
{
try
{
if (IsStop)
{
Stop();
return;
}
timer.Cancel();
var resultIds = await DB.getUnresolvedData();
if (resultIds.IsNullOrEmpty())
{
return;
}
var json = await server.RequestSTatus(resultIds);
if (string.IsNullOrEmpty(json))
return;
Model result = JsonConvert.DeserializeObject<Model>(json);
UpdateDB(result);
}
catch (Exception ex)
{
}
finally
{
Start();
}
}
public void Start()
{
if (IsStop) return;
timer = ThreadPoolTimer.CreatePeriodicTimer(async (t) =>
{
await DoWork();
}, TimeSpan.FromSeconds(10));
}
The question is ,how I can wait until "Job" is finished and then start again ?

You can do it little bit different. but code will be more clear and you will use ThreadPool anyway. Just use Task.Delay for timer. Something like this.
private bool isRunning;
public async void Start()
{
if (isRunning)
return;
isRunning = true;
while (isRunning)
{
await DoWork();
//wait period.
await Task.Delay(TimeSpan.FromSeconds(10));
if (!isRunning)
return;
}
}
private async Task DoWork()
{
//Do your work here.
}
public void Stop()
{
isRunning = false;
}
It is just a general idea.

Related

How to keep a System.Timers.Timer stopped during ElapsedEvent

I am using System.Timers.Timer and every x seconds I need to perform some tasks in an ElapsedEvent method. While I am performing my tasks in the ElapsedEvent method, I want the timer to be stopped. However, I have another method that can start the timer, which can be called while the ElapsedEvent is running. My code looks something like this:
class MyClass {
Timer myTimer;
public MyClass {
myTimer = new System.Timers.Timer();
// init timer code here...
}
public void ElapsedEventTask(object source, ElapsedEventArgs e) {
myTimer.Enabled = false;
try
{
// do my tasks
}
catch
{
...
}
finally
{
myTimer.Enabled = true;
}
}
}
public void AnotherMethod() {
// do some things
myTimer.Enabled = true;
}
How do I prevent AnotherMethod from starting the timer while I'm completing the task in ElapsedEventTask?
You can add a variable that indicate if the task is running. Finaly to be thread safe, you need to use lock when this variable is used in with myTimer.Enabled :
class MyClass
{
object syncEnableRunning = new object();
bool running
Timer myTimer;
public void ElapsedEventTask(object source, ElapsedEventArgs e)
{
lock(syncEnableRunning)
{
running = true;
myTimer.Enabled = false;
}
try { /*do my tasks*/}
catch { ... }
finally
{
lock(syncEnableRunning)
{
myTimer.Enabled = true;
running = false;
}
}
}
public void AnotherMethod()
{
// do some things
lock(syncEnableRunning)
{
if(!running)
{
myTimer.Enabled = true;
}
}
}
}
According to the documentation the System.Timers.Timer class is not thread-safe, so it's not safe to touch its Enabled property from multiple threads without synchronization (doing so results to undefined behavior). Vernou's answer shows how to synchronize the threads by using locks, but personally I am a bit nervous with trying to enforce a non-overlapping execution policy using a mechanism that apparently was designed to be re-entrant. So my suggestion is to ditch the System.Timers.Timer, and use instead an asynchronous loop, controlled by Stephen Cleary's PauseTokenSource mechanism:
class MyClass
{
private readonly CancellationTokenSource _cts;
private readonly PauseTokenSource _pts;
public Task Completion { get; private set; }
public MyClass(TimeSpan interval)
{
_cts = new CancellationTokenSource();
_pts = new PauseTokenSource();
_pts.IsPaused = true;
Completion = Task.Run(async () =>
{
try
{
while (true)
{
await _pts.Token.WaitWhilePausedAsync(_cts.Token);
var delayTask = Task.Delay(interval, _cts.Token);
/* Do my tasks */
await delayTask;
}
}
catch (OperationCanceledException)
when (_cts.IsCancellationRequested) { } // Ignore
});
}
public void Start() => _pts.IsPaused = false;
public void Stop() => _pts.IsPaused = true;
public void Complete() => _cts.Cancel();
}
The PauseTokenSource is the controller of a PauseToken, a similar concept with the CancellationTokenSource/CancellationToken combo. The difference is that the CancellationTokenSource can be canceled only once, while the PauseTokenSource can be paused/unpaused multiple times. This class is included in the AsyncEx.Coordination package.
The MyClass exposes a Complete method that terminates the asynchronous loop, and a Completion property that can be awaited. It is a good idea to await this property before closing the program, to give to any active operation the chance to complete. Otherwise the process may be killed in the middle of a background execution, with unpredictable consequences.
I would create a one shot timer, which you then need to start again at the end of your timer function.
myTimer = new System.Timers.Timer();
myTimer.AutoReset = false;
public void ElapsedEventTask(object source, ElapsedEventArgs e) {
...
finally
{
myTimer.Start();
}
}

C# WPF program button click run a task until another button click stop or until cancel token valid

I am creating a WPF app where I want to have a global bool im assuming, on the first button click I’ll set this bool to true and I want it to run a task (continuously call an API method) until I click the button again and it stops it. What would be the best way to do this?
private bool running = false;
private async void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
{
if (!running)
{
running = true;
}
else
running = false;
if (running)
{
RunningNrunnin(running);
//tradeClient.GetTradeHistory();
}
}
public void RunningNrunnin(bool running)
{
if (running)
{
Task task = new Task(() =>
{
while (running)
{
GetTradeHistory();
Thread.Sleep(2000);
}
});
task.Start();
}
}
Added Below
I would like to call a method over and over until the user creates a cancel request on a thread in the background. I currently had it so I can call a action (a counter) and update the GUI each second but when I try to do this same thing with a method call it executes only once.
// Here is the method I want to call continously until canceled
private async void HistoryTest()
{
cancellationToken = new CancellationTokenSource();
task = Task.Factory.StartNew(async () =>
{
while (true)
{
cancellationToken.Token.ThrowIfCancellationRequested();
await Client2.GetHistory();
await Task.Delay(2000);
}
}, cancellationToken.Token);
}
public async Task GetHistory()
{
try
{
var response = await Client.Service.GetDataAsync
(
ProductType.BtcUsd,
5,
1
);
}
catch(Exception)
{
throw;
}
}
I made a little console test app to test this so I had to change the method signatures (static) and can't use ButtonClick on a console. I simulated the button click by putting as sleep between the programatic "button click".
This might get you started.
private static bool isRunning = false;
private static int clickCounter = 0;
private static int iterationsCounter = 0;
static void Main(string[] args)
{
Console.WriteLine(“Start”);
for(int i = 0; i < 7; i++)
{
BtnTrade1_Buy_Click();
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine(“END”);
}
private static async Task BtnTrade1_Buy_Click()
{
iterationsCounter = 0;
isRunning = !isRunning;
Console.WriteLine($"Ha: {isRunning} {clickCounter++}");
await RunningNrunnin();
}
private static async Task RunningNrunnin()
{
await Task.Run(() => Runit());
}
private static void Runit()
{
while (isRunning)
{
GetTradeHistory();
System.Threading.Thread.Sleep(100);
}
}
private static void GetTradeHistory()
{
Console.WriteLine($"Hello Test {iterationsCounter++}");
}
Of course you wouldn't need all the counters and the Console.WriteLine() stuff. They are there to allow you to visualize what is happening.
Let me know if you need more info.
You don't need to do anything else inside the BtnTrade1_Buy_Click event handler, beyond toggling the isRunning field:
private bool _isRunning;
private void BtnTrade1_Buy_Click(object sender, RoutedEventArgs e)
{
_isRunning = !_isRunning;
}
The Task that is getting the trade history in a loop, needs to be started only once. You could start it in the Window_Loaded event. Storing the Task in a private field is a good idea, in case you decide to await it at some point, but if you are handling the exceptions inside the task it's not necessary.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_ = StartTradeHistoryLoopAsync(); // Fire and forget
}
private async Task StartTradeHistoryLoopAsync()
{
while (true)
{
var delayTask = Task.Delay(2000);
if (_isRunning)
{
try
{
await Task.Run(() => GetTradeHistory()); // Run in the ThreadPool
//GetTradeHistory(); // Alternative: Run in the UI thread
}
catch (Exception ex)
{
// Handle the exception
}
}
await delayTask;
}
}
Don't forget to stop the task when the window is closed.
private void Window_Closed(object sender, EventArgs e)
{
_isRunning = false;
}
This will stop the calls to GetTradeHistory(), but will not stop the loop. You may need to add one more private bool field to control the loop itself:
while (_alive) // Instead of while (true)

C# concurrent: Is it a good idea to use many AutoResetEvent?

Suppose there are many threads calling Do(), and only one worker thread handles the actual job.
void Do(Job job)
{
concurrentQueue.Enqueue(job);
// wait for job done
}
void workerThread()
{
while (true)
{
Job job;
if (concurrentQueue.TryDequeue(out job))
{
// do job
}
}
}
The Do() should wait until the job done before return. So I wrote the following code:
class Task
{
public Job job;
public AutoResetEvent ev;
}
void Do(Job job)
{
using (var ev = new AutoResetEvent(false))
{
concurrentQueue.Enqueue(new Task { job = job, ev = ev }));
ev.WaitOne();
}
}
void workerThread()
{
while (true)
{
Task task;
if (concurrentQueue.TryDequeue(out task))
{
// do job
task.ev.Set();
}
}
}
After some tests I found it works as expected. However I'm not sure is it a good way to allocate many AutoResetEvents, or is there a better way to accomplish?
Since all clients must wait a single thread to do the job, there is no real need for using a queue. So I suggest to use the Monitor class instead, and specifically the Wait/Pulse functionality. It is a bit low level and verbose though.
class Worker<TResult> : IDisposable
{
private readonly object _outerLock = new object();
private readonly object _innerLock = new object();
private Func<TResult> _currentJob;
private TResult _currentResult;
private Exception _currentException;
private bool _disposed;
public Worker()
{
var thread = new Thread(MainLoop);
thread.IsBackground = true;
thread.Start();
}
private void MainLoop()
{
lock (_innerLock)
{
while (true)
{
Monitor.Wait(_innerLock); // Wait for client requests
if (_disposed) break;
try
{
_currentResult = _currentJob.Invoke();
_currentException = null;
}
catch (Exception ex)
{
_currentException = ex;
_currentResult = default;
}
Monitor.Pulse(_innerLock); // Notify the waiting client that the job is done
}
} // We are done
}
public TResult DoWork(Func<TResult> job)
{
TResult result;
Exception exception;
lock (_outerLock) // Accept only one client at a time
{
lock (_innerLock) // Acquire inner lock
{
if (_disposed) throw new InvalidOperationException();
_currentJob = job;
Monitor.Pulse(_innerLock); // Notify worker thread about the new job
Monitor.Wait(_innerLock); // Wait for worker thread to process the job
result = _currentResult;
exception = _currentException;
// Clean up
_currentJob = null;
_currentResult = default;
_currentException = null;
}
}
// Throw the exception, if occurred, preserving the stack trace
if (exception != null) ExceptionDispatchInfo.Capture(exception).Throw();
return result;
}
public void Dispose()
{
lock (_outerLock)
{
lock (_innerLock)
{
_disposed = true;
Monitor.Pulse(_innerLock); // Notify worker thread to exit loop
}
}
}
}
Usage example:
var worker = new Worker<int>();
int result = worker.DoWork(() => 1); // Accepts a function as argument
Console.WriteLine($"Result: {result}");
worker.Dispose();
Output:
Result: 1
Update: The previous solution is not await-friendly, so here is one that allows proper awaiting. It uses a TaskCompletionSource for each job, stored in a BlockingCollection.
class Worker<TResult> : IDisposable
{
private BlockingCollection<TaskCompletionSource<TResult>> _blockingCollection
= new BlockingCollection<TaskCompletionSource<TResult>>();
public Worker()
{
var thread = new Thread(MainLoop);
thread.IsBackground = true;
thread.Start();
}
private void MainLoop()
{
foreach (var tcs in _blockingCollection.GetConsumingEnumerable())
{
var job = (Func<TResult>)tcs.Task.AsyncState;
try
{
var result = job.Invoke();
tcs.SetResult(result);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
}
public Task<TResult> DoWorkAsync(Func<TResult> job)
{
var tcs = new TaskCompletionSource<TResult>(job,
TaskCreationOptions.RunContinuationsAsynchronously);
_blockingCollection.Add(tcs);
return tcs.Task;
}
public TResult DoWork(Func<TResult> job) // Synchronous call
{
var task = DoWorkAsync(job);
try { task.Wait(); } catch { } // Swallow the AggregateException
// Throw the original exception, if occurred, preserving the stack trace
if (task.IsFaulted) ExceptionDispatchInfo.Capture(task.Exception.InnerException).Throw();
return task.Result;
}
public void Dispose()
{
_blockingCollection.CompleteAdding();
}
}
Usage example
var worker = new Worker<int>();
int result = await worker.DoWorkAsync(() => 1); // Accepts a function as argument
Console.WriteLine($"Result: {result}");
worker.Dispose();
Output:
Result: 1
From a synchronization perspective this is working fine.
But it seems useless to do it this way. If you want to execute jobs one after the other you can just use a lock:
lock (lockObject) {
RunJob();
}
What is your intention with this code?
There also is an efficiency question because each task creates an OS event and waits on it. If you use the more modern TaskCompletionSource this will use the same thing under the hood if you synchronously wait on that task. You can use asynchronous waiting (await myTCS.Task;) to possibly increase efficiency a bit. Of course this infects the entire call stack with async/await. If this is a fairly low volume operation you won't gain much.
In general I think would work, although when you say "many" threads are calling Do() this might not scale well ... suspended threads use resources.
Another problem with this code is that at idle times, you will have a "hard loop" in "workerThread" which will cause your application to return high CPU utilization times. You may want to add this code to "workerThread":
if (concurrentQueue.IsEmpty) Thread.Sleep(1);
You might also want to introduce a timeout to the WaitOne call to avoid a log jam.

DeadLock on task.Wait() with Task which edit UI

I'm trying to find some solutions to my problem here, but with no result (or I just do not get them right) so if anyone could help / explain i will be really gratefull.
I'm just developing a tool for system administrators using Win Form and now I need to create a continuous ping on the selected machine which is running on the background. There is an indicator for Online status on UI which I need to edit with background ping. So right now I'm in this state:
Class A (Win form):
ClassB activeRelation = new ClassB();
public void UpdateOnline(Relation pingedRelation)
{
//There is many Relations at one time, but form shows Info only for one...
if (activeRelation == pingedRelation)
{
if (p_Online.InvokeRequired)
{
p_Online.Invoke(new Action(() =>
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure
));
}
else
{
p_Online.BackgroundImage = (pingedRelation.Online) ? Properties.Resources.Success : Properties.Resources.Failure;
}
}
}
//Button for tunring On/Off the background ping for current machine
private void Btn_PingOnOff_Click(object sender, EventArgs e)
{
Button btn = (sender is Button) ? sender as Button : null;
if (btn != null)
{
if (activeRelation.PingRunning)
{
activeRelation.StopPing();
btn.Image = Properties.Resources.Switch_Off;
}
else
{
activeRelation.StartPing(UpdateOnline);
btn.Image = Properties.Resources.Switch_On;
}
}
}
Class B (class thats represent relation to some machine)
private ClassC pinger;
public void StartPing(Action<Relation> action)
{
pinger = new ClassC(this);
pinger.PingStatusUpdate += action;
pinger.Start();
}
public void StopPing()
{
if (pinger != null)
{
pinger.Stop();
pinger = null;
}
}
Class C (background ping class)
private bool running = false;
private ClassB classb;
private Task ping;
private CancellationTokenSource tokenSource;
public event Action<ClassB> PingStatusUpdate;
public ClassC(ClassB classB)
{
this.classB = classB;
}
public void Start()
{
tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
ping = PingAction(token);
running = true;
}
public void Stop()
{
if (running)
{
tokenSource.Cancel();
ping.Wait(); //And there is a problem -> DeadLock
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
private async Task PingAction(CancellationToken ct)
{
bool previousResult = RemoteTasks.Ping(classB.Name);
PingStatusUpdate?.Invoke(classB);
while (!ct.IsCancellationRequested)
{
await Task.Delay(pingInterval);
bool newResult = RemoteTasks.Ping(classB.Name);
if (newResult != previousResult)
{
previousResult = newResult;
PingStatusUpdate?.Invoke(classB);
}
}
}
So the problem is in deadlock when I cancel token and Wait() for task to complete -> it's still running, but While(...) in task is finished right.
You have a deadlock because ping.Wait(); blocks UI thread.
You should wait for task asynchronously using await.
So, if Stop() is event handler then change it to:
public async void Stop() // async added here
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
If it is not:
public async Task Stop() // async added here, void changed to Task
{
if (running)
{
tokenSource.Cancel();
await ping; // await here
ping.Dispose();
tokenSource.Dispose();
}
running = false;
}
As mentioned by #JohnB async methods should have Async suffix so, the method should be named as StopAsync().
Similar problem and solution are explained here - Do Not Block On Async Code
You should avoid synchronous waiting on tasks, so you should always use await with tasks instead of Wait() or Result. Also, as pointed by #Fildor you should use async-await all the way to avoid such situations.

Async friendly DispatcherTimer wrapper/subclass

I have a DispatcherTimer running in my code that fire every 30 seconds to update system status from the server. The timer fires in the client even if I'm debugging my server code so if I've been debugging for 5 minutes I may end up with a dozen timeouts in the client. Finally decided I needed to fix this so looking to make a more async / await friendly DispatcherTimer.
Code running in DispatcherTimer must be configurable whether it is reentrant or not (i.e. if the task is already running it should not try to run it again)
Should be task based (whether or not this requires I actually expose Task at the root is a gray area)
Should be able to run async code and await on tasks to complete
Whether it wraps or extends DispatcherTimer probably doesn't really matter but wrapping it may be slightly less ambiguous if you don't know how to use it
Possibly expose bindable properties for IsRunning for UI
Here's what I came up with.
SmartDispatcherTimer Extends DispatcherTimer (was easiest way to get this up and running)
Has a TickTask property to provide a Task to handle the logic
Has an IsReentrant property (of course the whole point is that I want it to not be reentrant so normally this is false)
It assumes anything you're calling is fully awaitable - or you'd end up losing the reentrancy protection benefits
Usage:
var timer = new SmartDispatcherTimer();
timer.IsReentrant = false;
timer.Interval = TimeSpan.FromSeconds(30);
timer.TickTask = async () =>
{
StatusMessage = "Updating..."; // MVVM property
await UpdateSystemStatus(false);
StatusMessage = "Updated at " + DateTime.Now;
};
timer.Start();
Here's the code. Would love to hear any thoughts on it
public class SmartDispatcherTimer : DispatcherTimer
{
public SmartDispatcherTimer()
{
base.Tick += SmartDispatcherTimer_Tick;
}
async void SmartDispatcherTimer_Tick(object sender, EventArgs e)
{
if (TickTask == null)
{
Debug.WriteLine("No task set!");
return;
}
if (IsRunning && !IsReentrant)
{
// previous task hasn't completed
Debug.WriteLine("Task already running");
return;
}
try
{
// we're running it now
IsRunning = true;
Debug.WriteLine("Running Task");
await TickTask.Invoke();
Debug.WriteLine("Task Completed");
}
catch (Exception)
{
Debug.WriteLine("Task Failed");
}
finally
{
// allow it to run again
IsRunning = false;
}
}
public bool IsReentrant { get; set; }
public bool IsRunning { get; private set; }
public Func<Task> TickTask { get; set; }
}

Categories

Resources