Reputation: 4919
I need to call async operation every 5 seconds, because operation sometimes can be longer I figured out to call method, then after I get result wait 5 seconds and call it again.
In main class I have something like this:
public partial class Form1 : Form
{
private Timer loopTimer;
public Form1()
{
InitializeComponent();
loopTimer = new Timer() { Interval = 10000 /*10 seconds*/};
loopTimer.Tick += loopTimer_Tick;
EWS.Instance.DoOperation(Operation_OK, Operation_ERROR);
}
private void Operation_OK(int count)
{
Console.WriteLine("timer start");
loopTimer.Start();
Console.WriteLine("timer enabled: " + loopTimer.Enabled);
Console.WriteLine("async result : " + count);
}
private void Operation_ERROR(string err)
{
MessageBox.Show(err);
}
private void loopTimer_Tick(object sender, EventArgs e)
{
Console.WriteLine("tick");
loopTimer.Stop();
EWS.Instance.DoOperation(Operation_OK, Operation_ERROR);
}
}
My EWS class looks like this:
class EWS : SingletonBase<EWS>
{
private EWS()
{
}
private int LongRunningMethod(Action<string> error)
{
Console.WriteLine("5 seconds operation");
Thread.Sleep(5000);
int unreadCount = 100;
return unreadCount;
}
public class CommandAndCallback<TSuccess, TError>
{
public TSuccess Success { get; set; }
public TError Error { get; set; }
public Func<Action<string>, int> Delegate { get; set; }
}
public void DoOperation(Action<int> success, Action<string> error)
{
Func<Action<string>, int> dlgt = LongRunningMethod;
CommandAndCallback<Action<int>, Action<string>> config = new CommandAndCallback<Action<int>, Action<string>>() { Success = success, Error = error, Delegate = dlgt };
dlgt.BeginInvoke(error, MyAsyncCallback, config);
}
public void MyAsyncCallback(IAsyncResult ar)
{
int s;
CommandAndCallback<Action<int>, Action<string>> config = (CommandAndCallback<Action<int>, Action<string>>)ar.AsyncState;
s = config.Delegate.EndInvoke(ar);
Console.WriteLine(s);
if (s > -1)
config.Success(s);
}
}
I'm able to call my method async, handle errors, but I don't know why Im unable to call it again after 5 seconds.
loopTimer_Tick
isn't called after I call loopTimer.Start();
I rewrite this couple of times and every time I can't get that timer to work.
I need to call method (that calls exchange server) in a loop but with time break between calls, if there are better ways to do this please write :)
Upvotes: 1
Views: 862
Reputation: 941465
The System.Windows.Forms.Timer class is not thread-safe. The specific failure here is that you call its Start() method on a thread pool thread. That creates a hidden window that provides the Tick event but its being created on the wrong thread. One that doesn't pump a message loop so the Tick event is never raised.
A possible workaround is to call Start() on the main thread, in the DoOperation() method for example. Or to use a System.Timers.Timer instead, beware however that its Elapsed event handler runs on an arbitrary threadpool thread so you cannot directly access the UI from it. You ought to do something about actually canceling the operation, this kind of code tends to get simpler if you use the BackgroundWorker or Task class.
Upvotes: 2