Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有已完成事件的异步方法_C#_.net_Asynchronous - Fatal编程技术网

C# 具有已完成事件的异步方法

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)

我使用.NET4.0,并试图找出如何使用异步方法等待DocumentCompleted事件完成并返回值。上面是我的原始代码,在这个场景中如何将其转换为异步/等待模型

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();