Application.DoEvents vs在循环中等待Task.Delay

Joh*_*ntu 3 c# multithreading task doevents async-await

令我不满的是,我需要在我的一个应用程序中使用WebBrowser控件.

我还需要做的一件事是等待元素变为可见/类更改/等,这在DocumentCompleted事件被触发后很好地发生,使得事件在我的情况下接近无用.

所以目前我有类似......

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

现在我已经阅读了多个DoEvents()邪恶的地方并且可能会导致很多问题,所以我考虑用Task.Delay()这样的方式替换它:

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

所以我的问题是,除了明显的事实,Thread.Sleep()意志阻止1ms的事件,并且Task.Delay()在上面的例子中有一个更大的延迟设置,这两种方法之间的实际差异是什么,这更好,为什么?

PS:请坚持这个问题,虽然我不一定会想到如何WebBrowser通过使用别的东西来解决控制问题本身的其他想法(想到js注入),这不是回答这个问题的地方,这个问题是关于这两位代码如何不同,哪些会被认为更好.

Ste*_*ary 6

这两种方法之间的实际差异是什么,这更好,为什么?

区别在于等待时如何处理消息.

DoEvents将安装嵌套的消息循环; 这意味着您的堆栈将具有(至少)两个消息处理循环.这导致了重入问题,IMO是避免的最大原因DoEvents.关于嵌套消息循环应该处理哪种事件存在无穷无尽的问题,因为该决策的双方都存在死锁,并且没有适合所有应用程序的解决方案.有关消息抽取的深入讨论,请参阅CLR博客文章中的经典公寓和抽水.

相反,await返回.所以它不使用嵌套的消息循环来处理消息; 它只允许原始的消息循环来处理它们.当async方法准备好恢复时,它将向消息循环发送一条特殊消息,该消息循环将继续执行该async方法.

因此,await实现并发,但没有固有的所有困难的重入问题DoEvents.await绝对是优越的方法.

基本上有一个持续的论点,DoEvents()是"更好",因为它不消耗线程池中的任何线程

好吧,await也不消耗线程池中的任何线程.繁荣.