【问题标题】:Async method hangs UI异步方法挂起 UI
【发布时间】:2016-09-07 08:51:00
【问题描述】:

我有一个窗口,它使用了一些异步方法。但是,只有当我在运行时创建它时,它才会挂起 UI(在这些操作期间),如下所示:

private void button_Click(object sender, RibbonControlEventArgs e)
{
    AboutWindow SettingsWindow = new AboutWindow();
    SettingsWindow.ShowDialog();
}

但是,如果我这样创建它:

private AboutWindow SettingsWindow = new AboutWindow();
private void button_Click(object sender, RibbonControlEventArgs e)
{
    SettingsWindow.ShowDialog();
}

一切似乎都正常,UI 没有挂起。

窗口内导致问题的方法如下:

private async void CallUpdateDownload()
{
    UpdaterStatus.CurrentUpdateStatus = await DownloadAndSaveInstallerAsync(UpdaterStatus.freshVersion);
}

public static async Task<UpdateStatus> DownloadAndSaveInstallerAsync(string NewVersion)

主要挂起发生在DownloadAndSaveInstallerAsync内的这个字符串上:

CloudBlockBlob blob = new CloudBlockBlob(new Uri(sas));
MemoryStream msRead = new MemoryStream();
blob.DownloadToStream(msRead); // HANGS HERE

问题是,为什么会这样?为什么在启动时创建窗口会阻止它挂起异步方法。

另一个问题是,我有另一个对象,它也使用DownloadAndSaveInstallerAsync 方法。这些对象是在运行时创建的,我无法做任何事情来防止这种挂起,在下载时发生。

如何防止 UI 在这种情况下挂起(在运行时创建对象)?

更多代码如下:

        private async void CallUpdateDownload()
                {
                    UpdaterStatus.CurrentUpdateStatus = await UpdaterLogic.DownloadAndSaveInstallerAsync(UpdaterStatus.freshVersion);
                }

更新逻辑:

        public static async Task<UpdateStatus> DownloadAndSaveInstallerAsync(string NewVersion)
                {
        //...
                          if (await SaveInstallerToMemoryAsync(NewVersion))
                                {
                                        return UpdateStatus.ReadyToInstall;
                                }
                                else
                                {
                                    return UpdateStatus.Error;
                                }
                }

    private static async Task<bool> SaveInstallerToMemoryAsync(string NewVersion)
            {
                        using (MemoryStream ms = await UpdaterClient.DownloadInstallerAsync(NewVersion))
                        using (FileStream fs = new FileStream(UpdaterSettings.Path), FileMode.Create))
                        {
                            if (ms == null) return false;
                            ms.Position = 0;
                            await ms.CopyToAsync(fs);
                            fs.Close();
                            ms.Close();
                            return true;
                        }
            }

更新客户端:

    public static async Task<MemoryStream> DownloadInstallerAsync(string version)
            {
                        string sas = await GetInstallerDownloadLinkAsync(version);

                        CloudBlockBlob blob = new CloudBlockBlob(new Uri(sas));

                            MemoryStream msRead = new MemoryStream();
                            blob.DownloadToStream(msRead);
                            return msRead;
            }

public static async Task<string> GetInstallerDownloadLinkAsync(string version)
        { 
                    HttpResponseMessage response = null;
                        response = await httpClient.GetAsync(UpdaterSettings.Link + version);
                    if (response.IsSuccessStatusCode)
                    {
                        var res = await response.Content.ReadAsStringAsync();
                        return res.Trim('"');
                    }
        }

【问题讨论】:

  • 你在DownloadAndSaveInstallerAsync里面等什么?
  • 方法CallUpdateDownload是在设置窗口创建时(在构造函数中...等)还是在窗口显示时运行?
  • 在里面,有一个方法await SaveInstallerToMemoryAsync(NewVersion),它的头是private static async Task&lt;bool&gt; SaveInstallerToMemoryAsync(string NewVersion)——就是这个方法,它可以和blobMemoryStream一起使用
  • CallUpdateDownload 被窗口内的按钮点击调用
  • 请在此处显示所有涉及的代码。

标签: c# winforms asynchronous


【解决方案1】:

您必须在任务/线程中执行长时间运行的部分并await 它们以避免阻塞。

public static async Task<MemoryStream> DownloadInstallerAsync(string version)
{
    string sas = await GetInstallerDownloadLinkAsync(version);

    CloudBlockBlob blob = new CloudBlockBlob(new Uri(sas));

    MemoryStream msRead = new MemoryStream();
    await Task.Run( () => blob.DownloadToStream(msRead) );
    return msRead;
}

更新

CloudBlockBlob 确实提供了一个您可以使用的DownloadToStreamAsync 方法

public static async Task<MemoryStream> DownloadInstallerAsync(string version)
{
    string sas = await GetInstallerDownloadLinkAsync(version);

    CloudBlockBlob blob = new CloudBlockBlob(new Uri(sas));

    MemoryStream msRead = new MemoryStream();
    await blob.DownloadToStreamAsync(msRead);
    return msRead;
}

【讨论】:

    猜你喜欢
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    相关资源
    最近更新 更多