使用 HttpWebRequest 时应用挂起

App hangs when use HttpWebRequest

我正在使用 C# 开发一个从 Internet 下载文件的应用程序(我不想使用后台下载器!) 这是 class 下载代码:

public class DL
{
    public event Progresses OnProgress;
    Stopwatch stopwatch = new Stopwatch();

    public async void Get(string url, StorageFile destinationFile)
    {
        stopwatch.Reset();
        stopwatch.Start();
        HttpWebRequest request = (HttpWebRequest)WebRequest.
            Create(url);
        HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync();
        long size = response.ContentLength;
        long downloaded = 0;


        using (Stream inputStream = response.GetResponseStream())
        using (Stream outputStream = await destinationFile.OpenStreamForWriteAsync())
        {
            byte[] buffer = new byte[1024];
            int bytesRead;
            do
            {
                bytesRead = inputStream.Read(buffer, 0, buffer.Length);
                downloaded += bytesRead;
                outputStream.Write(buffer, 0, bytesRead);
                int secondsRemaining = (int)(stopwatch.Elapsed.TotalSeconds
                    / downloaded * (size - downloaded));
                TimeSpan span = new TimeSpan(0, 0, 0, secondsRemaining);
                string remainTime = string.Format("{0}:{1}:{2}", span.Hours, span.Minutes, span.Seconds);
                OnProgress(remainTime);
            } while (bytesRead != 0);
        }
    }
}

public delegate void Progresses(string text);

这是下载文件的方法:

private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
    DL webGard = new DL();
    webGard.OnProgress += WebGard_OnProgress;
    StorageFile destinationFile = await KnownFolders.MusicLibrary
       .CreateFileAsync("r58.zip", CreationCollisionOption.GenerateUniqueName);
    string url = "my url";
    webGard.Get(url, destinationFile);
}

private async void WebGard_OnProgress(string text)
{
    System.Diagnostics.Debug.WriteLine(text);
    var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
    await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        textBlock.Text = text;
    });
}

当我按下下载按钮时,应用程序当前挂起并且在下载结束之前无法使用它,我想向用户显示剩余时间并且此代码适用于 [=20= 中的输出 windows ] 但 UI 被挂起,无法在 textBlock 中显示结果。

我该如何解决这个问题? 谢谢

这里的问题是您使用了所有正确的异步命令来启动,这很好。不幸的是,当您实际从流中读取数据时,您是在同步执行所有这些操作。这就是我的意思...

初始化流后,您就可以开始使用循环读取和写入数据。如果您查看 do/while 循环,您会发现所有操作都是同步完成的。此循环中有两个工作项导致您的应用挂起。这一行:

bytesRead = inputStream.Read(buffer, 0, buffer.Length);

和这一行:

outputStream.Write(buffer, 0, bytesRead);

在循环的每次迭代中,您将阻塞应用程序线程,同时等待服务器对下一个数据块的响应。这意味着您不仅在等待服务器回复,还在等待通过网络传输此数据的延迟。最重要的是,当您将这些数据写回您的文件时,您将被文件系统阻止。相反,您应该使用流的 ReadAsync and WriteAsync 方法。

这样,当您实际在内存中移动数据时,您实际上只会在很短的时间内阻塞主线程。然后你又回到等待流完成它们的操作,而你的应用程序 UI 线程可以自由地做它想做的事。

希望对您有所帮助!