Why doesn't async work on Elapsed event? - c#

Hi I wanted to call on a subroutine from within a function however it didn't seem possible:
private static void onEnergyTimer(object source, ElapsedEventArgs e)
{
if (energy < pastEnergy - 20) {
StartCoroutine(SendData());}
}
As a work around I tried this but when I try the code below I get the error "unexpected symbol void in class, struct, or interface member declaration"
using System;
using System.Collections;
using System.Collections.Generic;
using System.Timers;
using UnityEngine;
public class StickTimerBetaCoroutine2 : MonoBehaviour
{
public static float energy = 50.0f;
private static float pastEnergy = energy;
public void Update ()
{
System.Timers.Timer energyTimer = new System.Timers.Timer ();
energyTimer.Elapsed += new ElapsedEventHandler (onEnergyTimer);
energyTimer.Interval = 3000;
energyTimer.Enabled = true;
{
if (JoeysInputStick.flex <= 0) {
energy -= 8 * (Time.deltaTime);
} else if (JoeysInputStick.flex >= 1) {
energy += 2;
} else if (JoeysInputStick.flex >= 2) {
energy += 5;
} else if (JoeysInputStick.flex >= 3) {
energy += 7;
} else if (JoeysInputStick.flex >= 4) {
energy += 9;
} else if (JoeysInputStick.flex >= 5) {
energy += 11;
} else if (JoeysInputStick.flex >= 6) {
energy += 13;
}
}
energy = Mathf.Clamp (energy, 0.0F, 600.0F);
RectTransform rTrans = (RectTransform)transform.GetComponent<RectTransform> ();
rTrans.sizeDelta = new Vector2 (200, energy);
}
private async void onEnergyTimer (object source, ElapsedEventArgs e)
{
if (energy < pastEnergy - 20) {
JoeysInputStick.SendTensOn ();
await Task.Delay (800);
JoeysInputStick.SendTensOff ();
}
pastEnergy = energy;
}
}

Remove Async Keyword
You shouldn't have the async keyword on that function. The Elapsed timer event is not looking for an Async method.
It looks like you wanted to refer directly to the pre-existing private static void onEnergyTimer. I assume you got that code snippet using a decompiler (ilspy or other), and couldn't change that code.
Why async doesn't work on an event handler
Event handlers generally don't support/expect to be calling an async method. I'm not sure, but I think the one you are using uses the Windows Message pump to queue the message to action the Elapsed signal. The Windows Message pump is there so that all UI interactions occur on the Main Thread which owns the control handles.
The async pattern is not for Timers. Instead, if you did want an async equivelent of a delay, you would instead mark the Update function as Async, and then instead of using the Timer and Elapsed function, you would add the lines:
await Task.Delay(3000); //Here's your delay
if (energy < pastEnergy - 20) {
JoeysInputStick.SendTensOn ();
await Task.Delay (800);
JoeysInputStick.SendTensOff ();
}
pastEnergy = energy;
Mark third-party onEnergyTimer function as Public
Rather than implement your own accessible onEnergyTimer function, you could edit the third-party DLL (presumably), changing the function to public. There are many ways to do this, one is to request the third-party to update their accessibility of that function, another technical solution is to use the Mono Cecil library (I can go into more detail if required, just leave a comment) - this would work best if you have control over upgraded DLLs, and if you definitely want their version of that function for consistency.

You're trying to place your method signature outside of any class, where the compiler expects you to be declaring a class, struct, or interface. Move your method inside of your class.
public class MyClass
{
...
private async void onEnergyTimer(object source, ElapsedEventArgs e)
{
if (energy < pastEnergy - 20) {
JoeysInputStick.SendTensOn();
await Task.Delay(800);
JoeysInputStick.SendTensOff();
}
pastEnergy = energy;
}
}
This mistake can often happen because your curly-braces aren't matching properly. If you think your method is declared inside your class, inspect your braces to see if you've got a closing brace } where you didn't mean to.

I got some help with this and it turns out the issue was being caused by the editor (mono develop) and the version of .net that was being used by Unity (game engine). I wasn't able to change either of these factors so we used the code below as a work around:
private Queue energyQueue = new Queue();
private bool tensInAction = false;
private void onEnergyTimer (object source, ElapsedEventArgs e)
{
float maxEnergy = 0;
foreach (System.Object reading in (IEnumerable) energyQueue) {
if ((float) reading > maxEnergy) {
maxEnergy = (float) reading;
}
}
if (energy < maxEnergy - 20 && !tensInAction) {
energyQueue.Clear();
tensInAction = true;
JoeysInputStick.SendTensOn ();
System.Timers.Timer tensTimer = new System.Timers.Timer ();
tensTimer.Elapsed += new ElapsedEventHandler (onTensTimer);
tensTimer.Interval = 800;
tensTimer.Enabled = true;
}
energyQueue.Enqueue(energy);
if (energyQueue.Count > 12) {
energyQueue.Dequeue();
}
}
private void onTensTimer (object source, ElapsedEventArgs e) {
JoeysInputStick.SendTensOff ();
tensInAction = false;
}

Related

Anyone have a good idea for a basic timer in C#?

Does anyone have a good idea for a timer? I've tried using the stopwatch but I must have done something wrong, I simply wish to have a int value go up once per second and have to ability to reset it.
This is my failed piece of code:
//Timer systemet
Stopwatch Timer = new Stopwatch();
Timer.Start();
TimeSpan ts = Timer.Elapsed;
double seconds = ts.Seconds;
//interval
if(seconds >= 8)
{
Text = Text + 1;
Timer.Stop();
}
I see you've tagged this question with XNA and MonoGame. Typically, in game frameworks like this you don't use typical timers and stopwatches.
In MonoGame you would normally do something like this:
private float _delay = 1.0f;
private int _value = 0;
protected override void Update(GameTime gameTime)
{
var deltaSeconds = (float) gameTime.ElapsedGameTime.TotalSeconds;
// subtract the "game time" from your timer
_delay -= deltaSeconds;
if(_delay <= 0)
{
_value += 1; // increment your value or whatever
_delay = 1.0f; // reset the timer
}
}
Of course, this is the absolute simplest example. You can get more fancy and create a custom class to do the same thing. This way you can create multiple timers. There are examples of this in MonoGame.Extended which you're welcome to borrow the code from.
Easiest way is to use System.Timers.Timer.
Example:
using System.Timers;
class Program
{
static void Main(string[] args)
{
Timer t = new Timer(_period);
t.Elapsed += TimerTick;
t.Start();
}
static void TimerTick(Object source, ElapsedEventArgs e)
{
//your code
}
}
If you need more variable Timer, you can use System.Threading.Timer (System.Timers.Timer is basically wrapper around this class).
Example:
using System.Threading;
class Program
{
static void Main(string[] args)
{
Timer t = new Timer(TimerTick, new AutoResetEvent(false), _dueTime, _period);
}
static void TimerTick(Object state)
{
//your code
}
}

On a blocking background worker and Application.DoEvents

I am trying to cancel a background worker if its currently running, and then start another.
I tried this first, there are more checks for cancel in the functions...
private void StartWorker()
{
if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); }
StartServerGetIP.RunWorkerAsync();
}
private void StartServerGetIP_DoWork(object sender, DoWorkEventArgs e)
{
StartFTPServer(Port, Ringbuf, sender as BackgroundWorker, e);
if ((sender as BackgroundWorker).CancellationPending) return;
GetIP(Ringbuf, sender as BackgroundWorker, e);
}
private void StartServerGetIP_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
return;
}
if (e.Result.ToString() == "Faulted")
{
tcs.SetResult(false);
return;
}
Client.IPAddress = e.Result.ToString();
tcs.SetResult(true);
}
This approach blocks if the worker is canceled on StartServerGetIP.RunWorkerAsync();
After this I found an ugly solution in
private void StartWorker()
{
if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); }
while(StartServerGetIP.IsBusy) { Application.DoEvents(); }
StartServerGetIP.RunWorkerAsync();
}
Is there a pattern I can implement that will allow me to async cancel the background worker and start another without calling Application.DoEvents?
EDIT: A cancel button is out of the question.
EDIT: For those asking about the inner methods...
private void StartFTPServer(SerialPort port, RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args)
{
Stopwatch timeout = new Stopwatch();
TimeSpan max = TimeSpan.FromSeconds(MaxTime_StartServer);
int time_before = 0;
timeout.Start();
while (!buffer.Return.Contains("Run into Binary Data Comm mode...") && timeout.Elapsed.Seconds < max.Seconds)
{
if (timeout.Elapsed.Seconds > time_before)
{
time_before = timeout.Elapsed.Seconds;
sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds));
}
if (sender.CancellationPending)
{
args.Cancel = true;
return;
}
}
port.Write("q"); //gets into menu
port.Write("F"); //starts FTP server
}
private void GetIP(RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args)
{
//if longer than 5 seconds, cancel this step
Stopwatch timeout = new Stopwatch();
TimeSpan max = TimeSpan.FromSeconds(MaxTime_GetIP);
timeout.Start();
int time_before = 0;
string message;
while (!(message = buffer.Return).Contains("Board IP:"))
{
if (timeout.Elapsed.Seconds > time_before)
{
time_before = timeout.Elapsed.Seconds;
sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds + MaxTime_StartServer));
}
if (timeout.Elapsed.Seconds >= max.Seconds)
{
args.Result = "Faulted";
return;
}
if (sender.CancellationPending)
{
args.Cancel = true;
return;
}
}
Regex regex = new Regex(#"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");
string IP = message.Remove(0, "Board IP: ".Length);
if (regex.IsMatch(IP))
{
args.Result = IP;
ServerAlive = true;
}
}
Might as well give you the ring buffer too..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FPGAProgrammerLib
{
class RingBuffer<T>
{
T [] buffer { get; set; }
int _index;
int index
{
get
{
return _index;
}
set
{
_index = (value) % buffer.Length;
}
}
public T Add
{
set
{
buffer[index++] = value;
}
}
public T Return
{
get
{
return (index == 0) ? (IsString() ? (T)(object)string.Empty : default(T)) : buffer[--index];
}
}
private bool IsString()
{
return (typeof(T) == typeof(string) || (typeof(T) == typeof(String)));
}
public RingBuffer(int size)
{
buffer = new T[size];
index = 0;
}
}
}
In your StartServerGetIP_DoWork method there's a StartFTPServer method. I assume you don't check in that method if a cancellation has been requested. The same thing applies to your GetIP method. Those are probably your blocking points. If you want to ensure to actually cancel the job, you need to check periodically if a cancellation has been requested. So I would suggest you use an async method for StartFTPServer and GetIP that will check if the background worker has a cancellation requested.
I don't know the exact implementation you did in the StartFTPServer method or the GetIP method. If you would like more details on how to refactor the code so it can be cancelled post the code.
Here's a simple way to effectively cancel an in-flight function that's operating on another thread by using Microsoft's Reactive Framework (Rx).
Start with a long-running function that returns the value you want:
Func<string> GetIP = () => ...;
And you have some sort of trigger - could be a button click or a timer, etc - or in my case I'm using a type from the Rx library.
Subject<Unit> trigger = new Subject<Unit>();
Then you can write this code:
IObservable<string> query =
trigger
.Select(_ => Observable.Start(() => GetIP()))
.Switch()
.ObserveOn(this);
IDisposable subscription =
query
.Subscribe(ip => { /* Do something with `ip` */ });
Now anytime that I want to initiate the function I can call this:
trigger.OnNext(Unit.Default);
If I initiate a new call while an existing call is running the existing call will be ignored and only the latest call (so long as it completes) will end up being produced by the query and the subscription will get it.
The query keeps running and responds to every trigger event.
The .ObserveOn(this) (assuming you're using WinForms) brings the result back on to the UI thread.
Just NuGet "System.Reactive.Windows.Forms" to get the bits.
If you want trigger to be a button click, do this:
IObservable<Unit> trigger =
Observable
.FromEventPattern<EventHandler, EventArgs>(
h => button.Click += h,
h => button.Click -= h)
.Select(_ => Unit.Default);

Using ThreadPoolTimer with Background Audio UWP

I am making a UWP app using the BackgroundAudioTask. My app is working very well. Now I want to add in a TextBlock the Current position of the Audio played.
I was doing this method before implementing the audio Task:
private TimeSpan TotalTime;
private DispatcherTimer timerRadioTime;
private void radioPlayer_MediaOpened(object sender, RoutedEventArgs e)
{
TotalTime = radioPlayer.NaturalDuration.TimeSpan;
// Create a timer that will update the counters and the time slider
timerRadioTime = new DispatcherTimer();
timerRadioTime.Interval = TimeSpan.FromSeconds(1);
timerRadioTime.Tick += TimerRadioTime_Tick;
timerRadioTime.Start();
}
private void TimerRadioTime_Tick(object sender, object e)
{
//Check if the audio finished calculate it's total time
if (radioPlayer.NaturalDuration.TimeSpan.TotalSeconds > 0)
{
if (TotalTime.TotalSeconds > 0)
{
// Updating timer
TimeSpan currentPos = radioPlayer.Position;
var currentTime = string.Format("{0:00}:{1:00}", (currentPos.Hours * 60) + currentPos.Minutes, currentPos.Seconds);
radioTimerBlock.Text = currentTime;
}
}
}
When I implemented the Background Task it gave me an Exception. After researching I saw a suggestion of using ThreadPoolTimer instead of dispatcherTimer.
I tried writing this code (following this solution: Clock program employing ThreadPoolTimer C# uwp)
ThreadPoolTimer timer;
// for displaying time only
private void CurrentPlayer_MediaOpened(MediaPlayer sender, object args)
{
_clockTimer_Tick(timer);
}
private async void _clockTimer_Tick(ThreadPoolTimer timer)
{
var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
// Your UI update code goes here!
if (CurrentPlayer.NaturalDuration.TotalSeconds > 0)
{
TimeSpan currentPos = CurrentPlayer.Position;
var currentTime = string.Format("{0:00}:{1:00}", (currentPos.Hours * 60) + currentPos.Minutes, currentPos.Seconds);
CurrentPosition.Text = currentTime;
}
});
}
This is obviously not working. The app enters the method without updating my UI. I really don't understand what timer should be. Any Idea on how to make it run?
Solution:
ThreadPoolTimer timer;
// for displaying time only
private void CurrentPlayer_MediaOpened(MediaPlayer sender, object args)
{
timer = ThreadPoolTimer.CreatePeriodicTimer(_clockTimer_Tick, TimeSpan.FromSeconds(1));
}
private async void _clockTimer_Tick(ThreadPoolTimer timer)
{
var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () =>
{
// Your UI update code goes here!
if (CurrentPlayer.NaturalDuration.TotalSeconds < 0)
{
TimeSpan currentPos = CurrentPlayer.Position;
var currentTime = string.Format("{0:00}:{1:00}", (currentPos.Hours * 60) + currentPos.Minutes, currentPos.Seconds);
CurrentPosition.Text = currentTime;
}
});
}

Implement multiple timers in C#

I'm working on a windows forms app where I have several so called "services" that poll data from various services like Twitter, Facebook, Weather, Finance. Now each of my services has its individual polling interval setting so I was thinking I could implement a System.Windows.Forms.Timer for each of my services and set its Interval property accordingly so that each timer would fire an event at the preset interval that will cause the service to pull new data preferably async through a BackgroundWorker.
Is this the best way to do it? or will it slow down my app causing performance issues. Is there a better way of doing it?
Thanks!
You can do it with one Timer, just needs smarter approach to interval:
public partial class Form1 : Form
{
int facebookInterval = 5; //5 sec
int twitterInterval = 7; //7 sec
public Form1()
{
InitializeComponent();
Timer t = new Timer();
t.Interval = 1000; //1 sec
t.Tick += new EventHandler(t_Tick);
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
facebookInterval--;
twitterInterval--;
if (facebookInterval == 0)
{
MessageBox.Show("Getting FB data");
facebookInterval = 5; //reset to base value
}
if (twitterInterval == 0)
{
MessageBox.Show("Getting Twitter data");
twitterInterval = 7; //reset to base value
}
}
}
you do not really need BackgroundWorker, as WebClient class has Async methods.
so you may simply have one WebClient object for each of your "service" and use code like this:
facebookClient = new WebClient();
facebookClient.DownloadStringCompleted += FacebookDownloadComplete;
twitterClient = new WebClient();
twitterClient.DownloadStringCompleted += TwitterDownloadComplete;
private void FacebookDownloadComplete(Object sender, DownloadStringCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
{
string str = (string)e.Result;
DisplayFacebookContent(str);
}
}
private void OnFacebookTimer(object sender, ElapsedEventArgs e)
{
if( facebookClient.IsBusy)
facebookClient.CancelAsync(); // long time should have passed, better cancel
facebookClient.DownloadStringAsync(facebookUri);
}

FOR LOOP within Callback procedure in System.Threading.Timer causes it to fire only ONCE

At the moment I have a For loop within a Callaback procedure :
private void InitializeComponent()
{
var timer = new System.Threading.Timer(TimerProc, null, 60000, 60000);
}
public void TimerProc(object state)
{
for (int i = 0; i <= 6; i++)
{
//Do something here
}
}
TimerProc only fires ONCE despite the fact that I have it set to repeat every 60000 milliseconds. It looks like that the operations within the loop are no longer executed after they are completed the first time around.
However, if I remove the For loop and have some other action like print to a console line or write to a text file then TimerProc repeats as expected.
Why is this the case and why does a loop with in a Callback procedure stop it from executing after its initial firing?
I have made a workaround by using a while(true) infinte loop and using the Thread.Timer to pause the process after x amount of milliseconds.
My new vised code with the while loop looks like this:
private void InitializeComponent()
{
processthread = new Thread(new ThreadStart(DoSomething));
processthread.Start();
}
public void DoSomething()
{
while(true)
{
for (int i = 0; i <= 6; i++)
{
//do something here
}
Thread.Sleep(5000);
}
}
I just want to know why callback procedure in the System.Thread.Timer cannot handle loops.
Cheers,
Stanley
From the code you've included in the question, it looks like you aren't storing a reference to timer anywhere. I could be wrong, but my guess is that you're not seeing the callback repeated because the timer object itself has been garbage collected. Assign a reference to the timer to a longer-lived variable (i.e., a field) and I'd wager that will resolve the problem.
In other words, I don't think the for loop has anything to do with what you're seeing (I'm not disputing the evidence, just proposing that it's coincidental and nothing else).
To test my hypothesis, I created the following very simple Windows Forms app:
using System;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Threading.Timer;
public class Program
{
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
class TestForm : Form
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
var timer = new Timer(TimerProc, null, 1000, 1000);
}
public void TimerProc(object state)
{
for (int i = 0; i <= 6; i++)
{
Console.WriteLine(DateTime.Now.ToLongTimeString());
}
}
}
I saw the current time print to the console for a little more than thirty seconds; then it stopped.
In contrast, the following change appears to resolve the problem.
class TestForm : Form
{
// Note: declare a field to store a reference to the timer.
Timer timer;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.timer = new Timer(TimerProc, null, 1000, 1000);
}
// ...
}
I started the program again after making the above change, and the timer has continued firing for a few minutes.
Update: After discussion with Dan Tao it's probably a GC problem due to the lack of a reference. Adding in the reference fixes the problem [uncomment lines 8 and 20]. The presence of a the 'for loop' in the question probably caused the collection - maybe by generating garbage which explains why removing it and replacing it with a simpler LOC worked for you in this particular instance.
using System;
using System.Threading;
namespace SO_TimerTroubles
{
class Program
{
//static Timer t;
static void Main(string[] args)
{
StartTimer(); //Works fine.
Console.WriteLine("Press Enter to BeginGC");
Console.ReadLine();
GC.Collect(); //Timer stops.
Console.ReadLine();
}
public static void StartTimer()
{
//t =
new Timer(TimerProc, null, 1000, 1000);
}
static public void TimerProc(object state)
{
Console.WriteLine("Timer Called");
for (int i = 0; i < 5; i++)
{
}
}
}
}
[Original Answer] The following works fine - I suspect the error is elsewhere.
class Program
{
static void Main(string[] args)
{
var timer = new Timer(TimerProc, null, 1000, 1000);
Console.ReadLine();
}
static public void TimerProc(object state)
{
Console.WriteLine("Called");
for (int i = 0; i <= 6; i++)
{
Console.WriteLine(i);
}
}
}

Categories