首页 文章

WebClient.DownloadDataAsync不是真正的异步?

提问于
浏览
1

我需要异步从同一页面获取数据,不要阻止主线程 .

我尝试使用类WebClient的DownloadDataAsync方法,但似乎它的行为不是真正的异步方式 .

为了测试这个,我制作代码

private void button1_Click(object sender, EventArgs e)
    {
        checkLink_async();
        Thread.CurrentThread.Join(5000);

        checkLink_async();
        Thread.CurrentThread.Join(5000);

        checkLink_async();
        Thread.CurrentThread.Join(5000);
    }


    /// <summary>
    /// Check the availability of IP server by starting async read from input sensors.
    /// </summary>
    /// <returns>Nothing</returns> 
    public void checkLink_async()
    {
        string siteipURL = "http://localhost/ip9212/getio.php";
        Uri uri_siteipURL = new Uri(siteipURL);

        // Send http query
        WebClient client = new WebClient();
        try
        {
            client.DownloadDataCompleted += new DownloadDataCompletedEventHandler(checkLink_DownloadCompleted_test);
            client.DownloadDataAsync(uri_siteipURL);

            tl.LogMessage("CheckLink_async", "http request was sent");
        }
        catch (WebException e)
        {
            tl.LogMessage("CheckLink_async", "error:" + e.Message);
        }
    }

    private void checkLink_DownloadCompleted_test(Object sender, DownloadDataCompletedEventArgs e)
    {
        tl.LogMessage("checkLink_DownloadCompleted", "http request was processed");
    }

日志的结果:

01:32:12.089 CheckLink_async           http request was sent
01:32:17.087 CheckLink_async           http request was sent
01:32:22.097 CheckLink_async           http request was sent
01:32:27.102 checkLink_DownloadComplet http request was processed
01:32:27.102 checkLink_DownloadComplet http request was processed
01:32:27.102 checkLink_DownloadComplet http request was processed

我期望每个启动的DownloadDataAsync方法将并行运行并在主线程代码runnig期间完成(我使用Thread.CurrentThread.Join在代码中模拟它) . 但似乎在button1_Click结束之前,没有任何DownloadDataAsync调用都已完成(尽管有足够的时间) .

有没有办法改变这种行为,或者我应该使用另一种方法?

2 回答

  • 0

    我认为你最好使用 WebClientTaskasync-await 它使你的代码更整洁:

    private async void button1_Click(object sender, EventArgs e)
    {
        await checkLink_async();  
        await checkLink_async();
        await checkLink_async();        
    }
    
    
    /// <summary>
    /// Check the availability of IP server by starting async read from input sensors.
    /// </summary>
    /// <returns>Nothing</returns> 
    public async Task checkLink_async()
    {
        string siteipURL = "http://localhost/ip9212/getio.php";
        // Send http query
        var client = new System.Net.WebClient();
        try
        {            
            Task t = client.DownloadDataTaskAsync(siteipURL);
    
            //tl.LogMessage("CheckLink_async", "http request was sent");
    
            await t;
    
            //tl.LogMessage("checkLink_DownloadCompleted", "http request was processed");
        }
        catch (System.Net.WebException e)
        {
           // tl.LogMessage("CheckLink_async", "error:" + e.Message);
        }
    }
    
  • 2

    对于您正在观察的内容,有一个简单的(r)解释 . DownloadDataAsync 在封面下使用 System.ComponentModel.AsyncOperation ,它在 DownloadDataAsync 操作开始时保持对 SynchronizationContext 当前的当前引用,并在完成时发回给它 . 这样可以确保在具有有效 SynchronizationContext 的应用程序(即Windows窗体,它看起来就是您正在使用的窗体)中启动下载的同一线程上引发 DownloadDataCompleted 事件 .

    由于我们将回发到已经被阻止的线程,因此完成处理程序必须等待它才能执行 .

    button1_Click 处理程序的开头引入 SynchronizationContext.SetSynchronizationContext(null); ,在按钮处理程序运行完成之前,您将看到完成按照原始期望执行 .

    EDIT

    正如@PeterDuniho所指出的那样, SetSynchronizationContext(null) 不应该在快速和肮脏的测试之外使用 . 一种更好的方法来证明围绕存在或缺少 SynchronizationContext 的理论是使用单独的测试应用程序(即,如果已经使用Windows窗体,启动一个快速的控制台项目,以查看代码在没有 SynchronizationContext 的情况下的行为) . 与 SyncronizationContext.Current 混淆的用途有限,可以让你陷入痛苦的世界 .

相关问题