There are different ways to solve the problem of waiting for a while without crashing the GUI thread.
1) System.Timers.Timer, for . NET Framework 2.0+
Since you have a constant and predefined period, a Timer can warn you every X time when the action should 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 newer technique, you can opt for async/await to create a Task that waits for a certain time. By working with asynchrony, your GUI thread should continue running 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 user problem, but I kept it as it can serve as reference for anyone looking for ways not to freeze the screen in Windows Forms applications.
Considering that by your example you want to make a loop and wait a while between each iteration without locking the Windows Forms window, I believe that there is no practical way to do it with the older libraries of Threads manipulation. Because of that, 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 turning the have-navigate part and take the result of navigation in one step just for your 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 on the code, but for those who don’t have much familiarity with the TPL, we basically use the Taskcompletionsource to manipulate our Thread in a more controlled way. With it, we can define that we should wait for the call to Setresult() to finish the 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 Windows Forms button click event method:
/// <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");
// 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 output:
02/02/2016 12:02:26 Iniciando i=[0]
02/02/2016 12:02:29 i=[0] https://www.google.com/?gws_rd=ssl https://www.google.com/?gws_rd=ssl
02/02/2016 12:02:31 Iniciando i=[1]
02/02/2016 12:02:34 i=[1] https://www.google.com/?gws_rd=ssl https://www.google.com/?gws_rd=ssl
02/02/2016 12:02:36 Iniciando i=[2]
02/02/2016 12:02:38 i=[2] https://www.google.com/?gws_rd=ssl https://www.google.com/?gws_rd=ssl
02/02/2016 12:02:40 Iniciando i=[3]
02/02/2016 12:02:43 i=[3] https://www.google.com/?gws_rd=ssl https://www.google.com/?gws_rd=ssl
02/02/2016 12:02:45 Iniciando i=[4]
02/02/2016 12:02:47 i=[4] https://www.google.com/?gws_rd=ssl https://www.google.com/?gws_rd=ssl
02/02/2016 12:02:50 Acabou!
Because we use async/await in the button click method, you may notice that the method does not block the Graphical Interface Thread at any time, and at the same time we can give a waiting time while running the loop.
We’re talking about a Windows Forms/WPF project, right? And what’s the real reason for your wait, depends on some condition or the answer to something? Please describe a little better the flow your program should follow.
– Diego Jeronymo
Edit: Program flow Windows Form -> 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(); however, I would like to take a 1-minute break from each url visit,.
– William