Thread - How to use it without freezing screen?

1

I have a system that traverses some html elements, collecting some links. I would like for a wait time, but whenever I use Thread.Sleep it freezes my program by the time set. I have already used Application.DoEvents (); and Thread.Join and none of them resolved.

And every time I put Sleep into the Thread, it does not work because they fire one after the other, I think the correct way should be to wait for one Thread to start another, but how to do that?

Each link I visit creates a thread, as per the code below.

Edit: Windows Form Program Flow - > I have a loop with N repetitions, inside this loop I visit a web page and collect some links, every time I enter a link I create a Thread and at the end of it I kill it with Application.ExitThread ();

asked by anonymous 29.01.2016 / 02:03

1 answer

2

There are different ways to solve the problem of waiting for a while without crashing the Thread of the graphical interface.

1) System.Timers.Timer , to. NET Framework 2.0 +

Since you have a constant and predefined period, a Timer can warn you at each X time when the action is to be performed:

private System.Timers.Timer timer;

private void runBrowserThread(Uri url)
{
    timer = new System.Timers.Timer(2000);
    timer.Elapsed += OnTimerElapsed;
    timer.Start();
}

private void OnTimerElapsed(Object source, System.Timers.ElapsedEventArgs e)
{
    // ação individual

    // quando você souber que chegou ao fim...
    if(end)
    {
        timer.Stop();
    }
}

2) Task.Delay , for .NET Framework 4.5 +

If you have the option to opt for a more recent technique, you can choose async / await to create a Task that waits for a certain time. By working with asynchrony, your graphical interface thread should continue to run normally:

private async Task runBrowserThread(Uri url)
{
    // espera até que a Task complete o Delay
    await Task.Delay(5000);

    // continua executando
}

===================================================== ==========================

Edited: My first answer by itself did not solve the problem of the user, but I kept it as a reference for anyone looking for ways to not freeze the screen in Windows Forms applications.

Given that you want to loop through your example and wait for a time between each iteration without crashing the Windows Forms window, I do not think there is a practical way to do it with older thread manipulation libraries. Because of this, I will use the Task Parallel Library (TPL) , which is just .NET 4.0+ to get better at working.

I would solve this problem by transforming the part of having to navigate and take the navigation result in a step just for its iteration. For this I would create a method like NavigateAsync ():

/// <summary>
/// Classe estática apenas para tornar mais bonita a chamada ao método assíncrono para quem executa a tarefa, poderia ser um método qualquer dentro do próprio Form ou uma classe separada
/// </summary>
public static class WebBrowserExtension
{
    /// <summary>
    /// Carrega os resultados de uma navegação no WebBrowser através de um método assíncrono
    /// </summary>
    /// <param name="webBrowser">Instância de WebBrowser</param>
    /// <param name="urlString">Caminho da URL a ser navegada</param>
    /// <returns></returns>
    public static async Task<WebBrowserDocumentCompletedEventArgs> NavigateAsync(this WebBrowser webBrowser, string urlString)
    {
        // precisamos dele para controlar o nosso contexto assíncrono, que não teríamos iniciando Threads
        var taskCompletionSource = new TaskCompletionSource<WebBrowserDocumentCompletedEventArgs>();

        WebBrowserDocumentCompletedEventHandler handler = delegate(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // indicamos o fim do TaskCompletionSource retornando o argumento do evento
            taskCompletionSource.SetResult(e);
        };

        webBrowser.DocumentCompleted += handler;

        webBrowser.Navigate(urlString);

        // com o await, podemos esperar que o evento DocumentCompleted nos retorne os valores
        var navigationResult = await taskCompletionSource.Task;

        // é interessante desregistrar esse evento, porque caso contrário ao longo do loop vamos cair várias vezes no handler para Tasks já finalizadas
        // se não desregistrarmos aqui, teríamos que verificar por taskCompletionSource.Task.IsCompleted até encontrarmos a chamada ao Task que estamos manipulando de fato
        webBrowser.DocumentCompleted -= handler;

        return navigationResult;
    }
}

I made some comments in the code, but for people who are not very familiar with TPL, we basically use TaskCompletionSource to manipulate our Thread in a more controlled way. With it, we were able to define that we should wait for the call to SetResult () to finish execution, returning a reference to Task to be used with async / await by others.

To solve the flow you described, I used as an example a click event method of a Windows Forms button:

/// <summary>
/// Método que inicia o processamento. Não precisa ser um evento de clique de botão, basta ser um método assíncrono para iniciar a verificação
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnIniciar_Click(object sender, EventArgs e)
{
    // como você disse que está em um loop, vamos exemplificar em um
    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(String.Format("{0} Iniciando i=[{1}]", DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), i));

        // navegamos e esperamos a resposta, mas mantendo uma escrita síncrona
        var navigationResult = await webBrowser1.NavigateAsync("http://www.google.com.br");

        // só por causa da forma como você fez no DocumentCompleted, comparando os dois
        Console.WriteLine(String.Format("{0} i=[{1}] {2} {3}", DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), i, webBrowser1.Url.ToString(), navigationResult.Url.ToString()));

        // força a espera por uns 2 segundos
        await Task.Delay(2000);
    }

    Console.WriteLine(String.Format("{0} Acabou!", DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss")));
}

Running this example, I had an exit:

02/02/2016 12:02:26 Iniciando i=[0]
02/02/2016 12:02:29 i=[0] https://www.google.com.br/?gws_rd=ssl https://www.google.com.br/?gws_rd=ssl
02/02/2016 12:02:31 Iniciando i=[1]
02/02/2016 12:02:34 i=[1] https://www.google.com.br/?gws_rd=ssl https://www.google.com.br/?gws_rd=ssl
02/02/2016 12:02:36 Iniciando i=[2]
02/02/2016 12:02:38 i=[2] https://www.google.com.br/?gws_rd=ssl https://www.google.com.br/?gws_rd=ssl
02/02/2016 12:02:40 Iniciando i=[3]
02/02/2016 12:02:43 i=[3] https://www.google.com.br/?gws_rd=ssl https://www.google.com.br/?gws_rd=ssl
02/02/2016 12:02:45 Iniciando i=[4]
02/02/2016 12:02:47 i=[4] https://www.google.com.br/?gws_rd=ssl https://www.google.com.br/?gws_rd=ssl
02/02/2016 12:02:50 Acabou!

Because we use async / await in the click method of the button, you may notice that the method does not block the Graphical Interface Thread at any time, and at the same time we are able to give a wait time during loop execution.

    
29.01.2016 / 02:57