Wait()方法导致UI线程挂起-何时应该使用Wait()方法

本文关键字:方法 Wait 何时应 UI 线程 挂起 | 更新日期: 2023-09-27 17:54:34

我有以下代码连接到SignalR Hub

    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() ?

Wait()方法导致UI线程挂起-何时应该使用Wait()方法

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

await StartListening();

然而,在其他一些情况下,什么都不做可能更好:

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

或者使用ContinueWith进行手动延续。由于StartListening方法捕获任何异常,因此忽略返回的Task并没有什么问题——正如您已经看到的那样。不过我建议叫它StartListeningAsync

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

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

所以我的问题是,什么时候我应该使用Wait()?哪些实例和场景应该使用Wait(),什么时候不应该使用Wait()?

混淆是由于Task类型用于两种几乎完全不同的事情。

Task最初是在。net 4.0中作为任务并行库的一部分引入的。通常,您将使用Parallel LINQ或Parallel类进行并行处理(其中使用下面的Task类型)。但是,在高级场景中,您可以直接使用Task类型。Task.Wait用于等待这些独立任务完成。

async/await在。net 4.5中被引入时,现有的Task类型几乎足够好,可以用作抽象的"未来"。因此,他们没有发明一些新的"未来"类型,而是稍微扩展了Task,以作为未来的工作。

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

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

(这里有一点交叉:你可以将并行工作视为异步工作,并且在像Console Main方法这样的罕见情况下,你确实需要阻塞异步任务;但是暂时忽略它们

这意味着Task的API沿着这些行拆分。StartWaitResultContinueWith等成员完全属于并行类型。在异步环境中,await更合适。

我在async介绍的底部有一个小表,其中有一些新的(异步)等价于旧的(并行)做事方式。

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

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