c #: using threads in a "windows forms" project

4

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?

    
asked by anonymous 17.06.2015 / 01:21

1 answer

1

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 (not camelCasing )
  • I moved the timer to builder
  • I renamed the class x to TimedProcess , to have a more meaningful name.
17.06.2015 / 09:47