Question :
In my application I have several “subprocesses”. All of them issue information in which they are displayed on the Form.
I used System.Windows.Forms.Timer
:
Class x {
public Timer timer {get; set;}
public void f()
{
timer = new Timer();
timer.Tick += new EventHandler(this.tick);
timer.Enabled = false;
timer.Interval = 1000;
timer.Start();
}
private async void tick(object sender, EventArgs e)
{
this.status = ProcessoStatus.TRABALHANDO;
this.timer.Stop();
await Task.Run(() => this.processo());
this.timer.Start();
this.status = ProcessoStatus.OCIOSO;
}
}
One of these subprocesses checks a webservice for updated data. But for this I need all other processes to stop.
// main thread
Class Y
{
public void a()
{
X obj1 = new X();
X obj2 = new X();
obj1.f();
obj2.f();
}
public sync()
{
obj1.timer.Enabled = false;
while (obj1.status != ProcessoStatus.OCIOSO)
{
// faz nada no loop, apenas aguarda o método tick terminar
}
obj2.timer.Enabled = false;
while (obj2.status != ProcessoStatus.OCIOSO)
{ }
// pega os dados novos do webservice
}
}
The problem is that the while, in many cases, hangs the main thread, causing the status to never be “idle.”
Any suggestions for improving this?
Answer :
The problem is that the System.Windows.Forms.Timer
tick runs on the UI thread – and the UI thread is waiting for the tick to finish. That is, a deadlock occurs.
The solution is to make the UI thread wait asynchronously . There are several ways to do this, one of which would be to have each instance of x
expose a Task
that represents the state of the process (and thus, the status
property is no longer required.
class TimedProcess {
public Timer Timer {get; private set;}
public Task Completed { get; private set; }
public TimedProcess()
{
timer = new Timer();
timer.Tick += new EventHandler(this.Tick);
timer.Enabled = false;
timer.Interval = 1000;
timer.Start();
}
private async void Tick(object sender, EventArgs e)
{
this.timer.Stop();
Completed = Task.Run(() => this.processo());
await Completed;
timer.Start();
}
}
// UI Thread
public async Task Sync()
{
obj1.Timer.Enabled = false;
await obj1.Completed;
obj2.timer.Enabled = false;
await obj2.Completed;
// pega os dados novos do webservice
}
So, the Sync
method will release the UI thread until the tick ends.
Note that I made some changes:
- C # method names are written in
PascalCasing
(notcamelCasing
) - I moved the timer to builder
- I renamed the class
x
toTimedProcess
, to have a more meaningful name.