如何以阻塞/同步方式下载文件?

时间:2009-11-03 01:54:38

标签: c# silverlight events webclient blocking

我对silverlight很新,并且非常惊讶地发现只能进行异步文件下载。好吧,我试图通过设置一个标志并等待它改变来反击这个行为。 这是我的简单代码

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        WebClient webClient = new WebClient();
        webClient.DownloadProgressChanged +=
            new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
        webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
        webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative));
        while (XmlStateStream == null) { }
        lblProgress.Content = "Done Loading";
    }
    void webClient_DownloadProgressChanged(object sender, 
        DownloadProgressChangedEventArgs e) {

        lblProgress.Content = "Downloading " + e.ProgressPercentage + "%";
    }
    volatile Stream XmlStateStream = null;
    void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            lblProgress.Content = "Error: " + e.Error.Message;
            return;
        }
        XmlStateStream = e.Result;

    } 

这导致Firefox实际冻结(当我在开发时做其他事情时非常讨厌)(顺便说一句,对firefox的称赞导致我测试它并且firefox冻结了,但我没有丢失我输入的内容在恢复之后)

我不明白为什么while(XmlStateStream==null){}导致冻结。锁或volatile是否有某些属性(除了我已有的属性)或者我在Silverlight页面生命周期的错误部分还是什么?

我真的很困惑为什么这不起作用。

此外,这是silverlight 3.0

4 个答案:

答案 0 :(得分:5)

最有可能的是,此代码在UI线程中运行,该线程处理所有Web浏览器与用户的交互。这就是为什么你不会发现任何阻塞操作的原因 - 因为任何阻塞都会以与你看到的完全相同的方式冻结UI!更重要的是,如果UI线程也处理网络IO(这是常见的),那么你将在这里死锁,因为你正在等待的异步操作永远不会完成。

我担心你只需将代码重写为由异步操作驱动的状态机。

答案 1 :(得分:2)

虽然您需要在Silverlight中使用异步特性,但您可以使用C#3语法将事情保持在一起: -

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    DownloadXmlStateStream();
}

void DownloadXmlStateStream()
{
    WebClient webClient = new WebClient();

    webClient.DownloadProgressChanged += (s, e) => {    
        lblProgress.Content = "Downloading " + e.ProgressPercentage + "%";
    }

    webClient.OpenReadCompleted += (s, e) => {
        if (e.Error != null)
        {
            lblProgress.Content = "Error: " + e.Error.Message;
        }
        else
        {
            XmlStateStream = e.Result;
            lblProgress.Content = "Done Loading";
        }           
    } 

    webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative));
}

答案 2 :(得分:0)

您需要使用DownloadFileCompleted事件。

删除此:

while (XmlStateStream == null) { }
lblProgress.Content = "Done Loading";

添加:

webClient.DownloadFileCompleted +=
    new DownloadFileCompletedEventHandler(webClient_DownloadFileCompleted);

和此:

void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventHandler) {
    lblProgress.Content = "Done Loading";
}

如果您确实必须进行同步下载,则需要“轮询”下载完成的次数较少。尝试从忙碌等待循环中拨打System.Threading.Thread.Sleep(),延迟50-250毫秒。

虽然这会减少代码浪费的CPU利用率,但它可能无法修复UI响应问题。这取决于调用您的MainPage_Loaded的线程是否是唯一可以调用UI更新事件的线程。如果是这种情况,那么在该处理程序返回之前,UI根本无法更新。

答案 3 :(得分:0)

通过阻止直到文件下载,你不仅要阻止你的silverlight应用程序的UI线程 - 你也会阻止浏览器的UI线程,看起来就像。

你真正想做的事(我认为)就是停止你的应用程序做任何事情,直到下载完成。尝试将此行添加到MainPage_Loaded:

LayoutRoot.IsHitTestVisible = false;

删除阻止while循环和完成的消息(最后两行)。

在webClient_OpenReadCompleted中,添加:

LayoutRoot.IsHitTestVisible = true;
lblProgress.Content = "Done Loading.";

一切都应该按我想要的方式运作。

这是完整的代码:

   void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
            WebClient webClient = new WebClient();
            webClient.DownloadProgressChanged +=
                    new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
            webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
            webClient.OpenReadAsync(new Uri("/trunk/internal/SilverLightInterface.ashx?xxid=XXX", UriKind.Relative));
            LayoutRoot.IsHitTestVisible = false;
    }
    void webClient_DownloadProgressChanged(object sender, 
            DownloadProgressChangedEventArgs e) {

            lblProgress.Content = "Downloading " + e.ProgressPercentage + "%";
    }

    void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
    {
            if (e.Error != null)
            {
                    lblProgress.Content = "Error: " + e.Error.Message;
                    return;
            }
            LayoutRoot.IsHitTestVisible = true;
            lblProgress.Content = "Done Loading.";

    }