Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Application.DoEvents vs await Task.Delay in a loop

Much to my discontent, I need to use a WebBrowser control in one of my apps.

One of the things I also need to do is wait for an element to become visible/class changes/etc, which happens well after the DocumentCompleted event is fired, making the event close to useless in my case.

So currently I have something like...

while (webBrowser.Document?.GetElementById("id")?.GetAttribute("classname") != "class")
{
    Application.DoEvents();
    Thread.Sleep(1);
}

Now I've read in multiple places that DoEvents() is evil and can cause lots of issues, so I thought about replacing it with Task.Delay() as such:

while (webBrowser.Document?.GetElementById("id")?.GetAttribute("classname") != "class")
{
    await Task.Delay(10);
}

So my question is, other than the obvious facts that the Thread.Sleep() will block events for 1ms and that the Task.Delay() has a bigger delay set in the example above, what are the actual differences between doing the two approaches, which is better and why?

PS: Please stick to the question, while I wouldn't necessarily mind other ideas on how to fix the WebBrowser control issue itself by using something else (js injection comes to mind), this is not the place to answer that, this question is about how these two bits of code differ and which would be considered better.

like image 917
JohnUbuntu Avatar asked Feb 03 '16 21:02

JohnUbuntu


2 Answers

what are the actual differences between doing the two approaches, which is better and why?

The differences are in how messages are processed while waiting.

DoEvents will install a nested message loop; this means that your stack will have (at least) two message processing loops. This causes reentrancy issues, which IMO is the biggest reason to avoid DoEvents. There's endless questions about which kinds of events the nested message loop should process, because there's deadlocks on both sides of that decision, and there's no solution that's right for all applications. For an in-depth discussion of message pumping, see the classic Apartments and Pumping in the CLR blog post.

In contrast, await will return. So it doesn't use a nested message loop to process messages; it just allows the original message loop to process them. When the async method is ready to resume, it will send a special message to the message loop that resumes executing the async method.

So, await enables concurrency but without all the really difficult reentrancy concerns inherent in DoEvents. await is definitely the superior approach.

Basically there's an ongoing argument that DoEvents() is "better" because it doesn't consume any threads from the thread pool

Well, await doesn't consume any threads from the thread pool, either. Boom.

like image 149
Stephen Cleary Avatar answered Sep 28 '22 02:09

Stephen Cleary


While await Task.Delay(10) is executing the UI can process events. While Thread.Sleep(1); is running the UI is frozen. So the await version is better and will not cause any major issues.

Clearly, spinning in a busy loop has the risk of burning CPU and battery. You seem to be aware of those drawbacks.

like image 28
usr Avatar answered Sep 28 '22 02:09

usr