【问题标题】:Wait() causes UI thread to hang - when should Wait() be used?Wait() 导致 UI 线程挂起 - 什么时候应该使用 Wait()?
【发布时间】:2014-03-28 20:30:30
【问题描述】:

我有以下代码连接到 SignalR 集线器

    private static async Task StartListening()
    {
        try
        {


            var hubConnection = new HubConnection("http://localhost:8080/");                
            IHubProxy hubProxy = hubConnection.CreateHubProxy("Broadcaster");
            hubProxy.On<EventData>("notifyCardAccessEvent", eventData =>
            {
                Log.Info(string.Format("Incoming data: {0} {1}", eventData.Id, eventData.DateTime));
            });
            ServicePointManager.DefaultConnectionLimit = 10;
            await hubConnection.Start();
            Log.Info("Connected");
        }
        catch (Exception ex)
        {
            Log.Error(ex);
        }
    }

在我的Form_Load 方法中,我有这个

StartListening();

但是,Resharper 提示我“考虑将 'await' 运算符应用于调用结果”

所以我这样做了:

Log.Info("Connecting to SignalR hub...");
StartListening().Wait();
Log.Info("Connected!");

但是,这会导致我的 UI 线程挂起,并且永远不会将 Connected! 打印到日志文件中。

所以我的问题是,我应该什么时候使用Wait()?什么情况下应该使用Wait(),什么时候不应该使用Wait()

【问题讨论】:

  • 所以,它说“考虑应用 await”,然后你做了一些不同的事情 - 为什么你决定不听从建议并实际使用 await
  • 啊啊啊是的!我忽略了这一点。我以为他们是一样的。感谢您指出这一点!

标签: c# .net task-parallel-library signalr async-await


【解决方案1】:

await 不是Wait。目前尚不清楚调用StartListening() 的代码是什么,但一种选择是await 它,正如建议的那样:

await StartListening();

但是,在其他一些情况下,最好什么都不做:

StartListening(); // drop the Task on the floor

或者也许使用ContinueWith 手动继续。由于StartListening 方法捕获任何异常,因此忽略返回的Task 并没有什么问题——所以你已经拥有了。不过,我建议称它为 StartListeningAsync

死锁的原因是,如果您使用Wait,您的 UI 线程会阻塞等待异步方法完成,但该异步方法正在捕获同步上下文,这意味着为了处理它尝试的每个延续进入 UI 线程 - 被阻塞...就可以了

【讨论】:

  • @the8thbit 远; Wait 是如果我要求你做某事,然后我就站在你身后,在你完成之前什么也不做。 await 是如果我要求你做某事,然后当你完成并且我不在做某事时来拍我的肩膀。有点儿。类比很烂;p这里的“同步上下文”是有点“如何引起我的注意”——但这有点牵强;p但为了说明:一种选择是告诉它不要捕获同步上下文 - 在那种的情况下,当你完成后,会去做我接下来想做的任何事情
  • @the8thbit 或者,根据具体情况,您可以在工作池的收件箱中留下“下一步去做”
【解决方案2】:

@MarcGravell 有正确答案;我只是要回答另一个问题:

所以我的问题是,我应该什么时候使用 Wait()?什么情况下应该使用Wait(),什么时候不应该使用Wait()?

混淆是因为Task 类型用于两个几乎完全不同的东西。

Task 最初是在 .NET 4.0 中作为任务并行库的一部分引入的。通常,您将使用 Parallel LINQ 或 Parallel 类进行并行处理(在下面使用 Task 类型)。但是,in advanced scenarios, you could use the Task type directlyTask.Wait 用于等待那些独立的任务完成。

async/await 在.NET 4.5 中引入时,现有的Task 类型几乎足以用作抽象的“未来”。因此,他们没有发明一些新的“未来”类型,而是将Task 稍微扩展为未来。

这将我们带到今天,Task 可以用作:

  • 并行计算中的一项工作。
  • 异步未来。

(有一点交叉:您可以将并行工作视为异步工作,并且在极少数情况下,例如 Console Main 方法,您确实需要阻止异步任务;但暂时忽略这些。)

这意味着Task 的API 沿这些线拆分。 StartWaitResultContinueWith 等成员非常坚定地属于平行的一面。在异步世界中,await 更合适。

我的async intro 底部有一张小桌子,上面有一些新的(异步)等价物,用于旧的(并行)做事方式。

【讨论】:

    【解决方案3】:

    您似乎误解了 Resharper 的消息。您调用了Task.Wait() 方法,而不是应用await 运算符。它们可能看起来相似,但它们的工作方式完全不同。

    这个不错的答案将提供有关差异的更多信息:https://stackoverflow.com/a/13140963/3465395

    【讨论】:

      猜你喜欢
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-20
      • 2011-08-08
      • 1970-01-01
      相关资源
      最近更新 更多