C# 具有已完成事件的异步方法
我使用.NET4.0,并试图找出如何使用异步方法等待DocumentCompleted事件完成并返回值。上面是我的原始代码,在这个场景中如何将其转换为异步/等待模型C# 具有已完成事件的异步方法,c#,.net,asynchronous,C#,.net,Asynchronous,我使用.NET4.0,并试图找出如何使用异步方法等待DocumentCompleted事件完成并返回值。上面是我的原始代码,在这个场景中如何将其转换为异步/等待模型 private class BrowserWindow { private bool webBrowserReady = false; public string content = ""; public void Navigate(string url)
private class BrowserWindow
{
private bool webBrowserReady = false;
public string content = "";
public void Navigate(string url)
{
xxx browser = new xxx();
browser.DocumentCompleted += new EventHandler(wb_DocumentCompleted);
webBrowserReady = false;
browser.CreateControl();
if (browser.IsHandleCreated)
browser.Navigate(url);
while (!webBrowserReady)
{
//Application.DoEvents(); >> replace it with async/await
}
}
private void wb_DocumentCompleted(object sender, EventArgs e)
{
try
{
...
webBrowserReady = true;
content = browser.Document.Body.InnerHtml;
}
catch
{
}
}
public delegate string AsyncMethodCaller(string url);
}
因此,我们需要一个方法,在触发
DocumentCompleted
事件时返回任务。对于给定的事件,只要您需要,就可以创建如下方法:
public static Task WhenDocumentCompleted(this WebBrowser browser)
{
var tcs = new TaskCompletionSource<bool>();
browser.DocumentCompleted += (s, args) => tcs.SetResult(true);
return tcs.Task;
}
@Servy有我一直在寻找的天才答案,但它并不适用于我的用例。由于事件处理程序试图在后续事件调用中设置
TaskCompletionSource
上的结果,我在多次引发事件时发现了错误
我从两个方面加强了他的回答。第一种方法是在第一次处理DocumentCompleted
事件后,取消订阅该事件
文档完成时的公共静态任务(此WebBrowser浏览器)
{
var tcs=new TaskCompletionSource();
browser.DocumentCompleted+=DocumentCompletedHandler;
返回tcs.Task;
void DocumentCompletedHandler(对象发送者,事件参数e)
{
browser.DocumentCompleted-=DocumentCompletedHandler;
tcs.SetResult(真);
}
}
注意,我在这里使用一个本地函数来捕获TaskCompletionSource
实例,它至少需要C#7.0
第二个增强是添加超时。我的特定用例是在单元测试中,我希望使等待特定事件具有确定性,而不是在出现问题时无限期地等待
我选择使用一个计时器来完成这项工作,并将其设置为只启动一次,然后在不再需要时停止计时器。或者可以在这里利用CancellationToken
来管理TaskCompletionSource
,但我觉得这需要维护人员更多地了解他们的用法,并且定时器更为普遍地理解
文档完成时的公共静态任务(此WebBrowser浏览器,int-timeoutin毫秒=500)
{
var tcs=new TaskCompletionSource();
var timeoutTimer=new System.Timers.Timer(timeoutintimeters);
timeoutTimer.AutoReset=false;
timeoutTimer.appeated+=(s,e)=>tcs.trysetconceled();
timeoutTimer.Start();
browser.DocumentCompleted+=DocumentCompletedHandler;
返回tcs.Task;
void DocumentCompletedHandler(对象发送者,事件参数e)
{
timeoutTimer.Stop();
browser.DocumentCompleted-=DocumentCompletedHandler;
tcs.TrySetResult(真);
}
}
注意:为了确保代码是线程安全的,我在这里使用了Try…
函数。这样可以确保即使在发生边缘案例交错执行时,也不会出现设置结果的错误
感谢您的出色解决方案,但有一个问题是我使用的是vs 2010和.net 4.0,因此我必须使用旧样式,而不是等待synctax(4.5)。我有点搞不清楚如何让它在4分钟内工作。0@namvo您明确表示希望使用async/await解决方案,因此我假设您使用的是兼容包。如果没有,则只需对方法返回的任务使用
ContinueWith
(4.0中的WhenDocumentCompleted
事件将起作用),并将文档完成后应运行的代码放在其中。为了方便起见,你很可能想使用lambda。这似乎是一个非常有用和重要的知识点,结果比我想象的要难找到。基本上,这似乎就是定义aynsc任务的方式,而不仅仅是由其他异步任务组成。在研究异步编程时,有没有一个更普遍的地方可以了解这一点?是否还有其他相关的知识(其他与TaskCompletionSource相关的类可以提供任务)?我想我也应该适当地设置Exception,但不知道我是否遗漏了其他内容。@BlueMonkMN关于这个主题的书很多,在线教程很多,博客很多,问题很多等等。找到有关该主题的信息一点也不难。@Servy我想我只是惊讶于它在Microsoft有关异步任务的文档()和其他通常讨论异步任务的文章中没有更明显的内容。我现在看到,谷歌搜索“自定义异步任务c#”比搜索“异步任务c#”给出的结果更相关。
await browser.WhenDocumentCompleted();