【问题标题】:Await stops execution of thread and never continues等待停止线程的执行并且永远不会继续
【发布时间】:2015-05-29 12:43:07
【问题描述】:

我有以下:

public async Task<bool> SearchForUpdatesAsync()
{
    return await TaskEx.Run(() =>
    {
    if (!ConnectionChecker.IsConnectionAvailable())
        return false;

    // Check for SSL and ignore it
    ServicePointManager.ServerCertificateValidationCallback += delegate { return (true); };

    var configurations = UpdateConfiguration.Download(UpdateConfigurationFileUri, Proxy);
    var result = new UpdateResult(configurations, CurrentVersion,
        IncludeAlpha, IncludeBeta);

    if (!result.UpdatesFound)
        return false;

    _updateConfigurations = result.NewestConfigurations;
    double updatePackageSize = 0;
    foreach (var updateConfiguration in _updateConfigurations)
    {
        var newPackageSize = GetUpdatePackageSize(updateConfiguration.UpdatePackageUri);
        if (newPackageSize == null)
            throw new SizeCalculationException(_lp.PackageSizeCalculationExceptionText);

        updatePackageSize += newPackageSize.Value;
        _packageOperations.Add(new UpdateVersion(updateConfiguration.LiteralVersion),
            updateConfiguration.Operations);
    }

    TotalSize = updatePackageSize;
    return true;
});
}

如您所见,我使用的是 Microsoft.Bcl。 现在在我的另一堂课中,我用普通的 void 编写了这段代码:

TaskEx.Run(async delegate
{
    // ...
    _updateAvailable = await _updateManager.SearchForUpdatesAsync();
     MessageBox.Show("Test");
});

我遇到的问题是它执行_updateAvailable = await _updateManager.SearchForUpdatesAsync(); 然后它没有继续线程,它只是停止,好像在那个调用之后什么都没有。过了一会儿,Visual Studio 也会告诉我这个:Thread ... exited with code 259,所以有些东西似乎还活着。

我通过它进行调试以搜索任何可能被吞下的异常,但什么都没有,一切正常,它执行 return 语句。 这就是我不明白的,我从来没有看到 MessageBox 和/或没有执行此行之外的代码。 在我和一些朋友交谈后,他们确认这不应该。我在实现 async-await 时犯了一个可怕的错误吗?

在此先感谢,实际上我只能说这些了,我没有更多信息,我希望尽可能提供任何提示和帮助。

【问题讨论】:

  • TaskEx 的意义何在?只需等待您的方法,您无需创建另一个仅再次同步调用该方法的任务
  • @MatthewFrontino 你的意思是上面第二个代码部分的TaskEx.Run?
  • 是的,你有一个等待任务的方法,它等待一个方法。你需要那么多层吗?
  • @MatthewFrontino 我想是的,如果没有异步修饰符,我不能等待该方法,或者我应该在“void”前面写那个修饰符来标记整个方法?还是根本不需要等待?那是我还没有理解的另一点,这是否意味着我的异步方法在运行其中的任务时已经在等待内容?这意味着我应该将类型从“Task”更改为“bool”,对吧?
  • 您是否尝试调用 Result 或使用 continue 方法

标签: c# winforms asynchronous async-await


【解决方案1】:

您遇到的主要问题是您不必要地将方法包装在 TaskEx.Run() 中,并且您可能在某处遇到了死锁。

您的方法签名当前是:

public async Task<bool> SearchForUpdatesAsync()

这意味着:

async --> 实际上没有做任何事情,但提供了一个“提示”,await 可能会在此方法中被调用,并且此方法可以用作可运行对象任务。
Task --> 该方法返回一个可在线程池上异步运行的可运行任务
&lt;bool&gt; --> 该方法在等待时实际上返回 bool。

await TaskEx.Run() 是不必要的,因为它表示运行此方法,然后在值可用之前不返回。这很可能会导致同步问题。删除此构造将使您的方法正常工作,但是您会注意到现在您甚至没有理由包含 async 运算符或 Task&lt;T&gt; 部分,因为无论如何该方法实际上是同步的。通常,如果您有要在其上调用 await 的方法,您只会在方法签名上使用 async 标识符。

相反,您有两个选择。

  1. 每当您想调用 SearchForUpdates() 时,都可以将其包装在 Task&lt;bool&gt;.Run() 中以异步运行(或 Bcl 等效项)
  2. 由于您使用的是 WinForms,因此最好使用 BackgroundWorker 并在其中调用此方法。

关于使用 async-await 模式,我认为这是一篇很棒的文章,可以用来确保您遵循最佳实践:https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

【讨论】:

  • 是的,这行得通,而且那个解释帮助我理解它,现在。谢谢!
【解决方案2】:

最好的做法是在你的层中一直保持异步,然后在最终使用站点调用 await 或更不理想的 .Wait() / .Result .

此外,请尽量将您的 UI 调用与后端工作分开,因为您可能会遇到同步性/线程上下文问题。

public class WinFormsCode
{
    private async Task WinsFormCodeBehindMethodAsync()
    {
        var updatesAvailable = await _updateManager.SearchForUpdatesAsync();
        MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
    }

    private void WinsFormCodeBehindMethodSync()
    {
        var updatesAvailable = _updateManager.SearchForUpdatesAsync().Result;
        MessageBox.Show("Updates Available: " + updatesAvailable.ToString());
    }
}

public class UpdateManager
{
    public async Task<bool> SearchForUpdatesAsync()
    {
        return true;
    }
}

【讨论】:

  • 这不是正确使用异步等待模式的方法。如果你去异步,那么你想一直去异步。 msdn.microsoft.com/en-us/magazine/jj991977.aspx
  • 嗯,我不太明白,如果我在 SearchForUpdatesAsync 中等待 SearchForUpdatesAsync,那么这会导致无限循环,不是吗?
  • @JNYRanger:异步链必须在某些 UI 上下文/框架的 UI 调用处结束。虽然始终首选完全异步,但在表示层同步运行也是可以接受的。
  • @Trade:您正在调用“_updateManager.SearchForUpdatesAsync”,这可能是一个外部调用。应该没有无限循环,除非 _updateManager 是对该方法所在类的引用。
  • 该方法包含在实例化“_updateManager”的类中。 ;) 如前所述,“await _updateManager.SearchForUpdatesAsync()”的调用是在另一个类中完成的。
猜你喜欢
  • 2021-02-05
  • 1970-01-01
  • 1970-01-01
  • 2019-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多