WebClient.DownloadDataAsync不是真正的异步?

时间:2014-11-19 23:00:22

标签: c# multithreading asynchronous webclient

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

我尝试使用类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");
    }

log:

的结果
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 :(得分:2)

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

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

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

修改

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

答案 1 :(得分:0)

我认为您最好使用Task WebClientasync-await的{​​{1}} API,这会使您的代码更加整洁:

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