【问题标题】:Call async method with callback from sync UI thread [duplicate]使用来自同步 UI 线程的回调调用异步方法 [重复]
【发布时间】:2016-07-11 10:58:36
【问题描述】:

我真的被困在这里...我有一个 XAML 页面 UI,并且希望在每次用户与 UI 交互时调用一个异步函数。
我使用 SignalR 进行网络:

public static class ProtocolClient
{
    private static HubConnection hubConnection;
    private static IHubProxy protocolHubProxy;

    public static async void connect(string server)
    {
        hubConnection = new HubConnection(server);
        protocolHubProxy = hubConnection.CreateHubProxy("ProtocolHub");
        protocolHubProxy.On<Body>("BodiesChanged", body =>
            //call a callback to return body
        );
        await hubConnection.Start(); //wait for connection
    }

    public static async void sendTouch(Touch touch)
    {
        Body body = await protocolHubProxy.Invoke<Body>("GetBodyForTouch", touch);
        //call a callback to return body
    }
}

用户界面:

public sealed partial class MainPage : Page
{
    [...]
    private void Canvas_PointerPressed(object sender, PointerRoutedEventArgs e)
    {
        [...]
        switch (ptrPt.PointerDevice.PointerDeviceType)
        {
            case Windows.Devices.Input.PointerDeviceType.Mouse:
                if (ptrPt.Properties.IsLeftButtonPressed)
                {
                    //call sendTouch
                }
                break;
            default:
                break;
        }
        [...]
    }
}

我需要一个可以修改 UI 的回调。如何从 UI 中调用 connect 和 sendTouch 并向它们传递回调?

【问题讨论】:

    标签: c# asynchronous asynccallback


    【解决方案1】:

    您不需要回调。只需在await hubConnection.Start(); 语句后添加代码即可。您的方法被“分割成多种方法”,并且在等待返回后将“继续”。 await 的作用类似于阻塞语句,但不会冻结 gui。

    public static async void connect(string server)
    {
        hubConnection = new HubConnection(server);
        protocolHubProxy = hubConnection.CreateHubProxy("ProtocolHub");
        protocolHubProxy.On<Body>("BodiesChanged", body =>
            //call a callback to return body
        );
        await hubConnection.Start(); //wait for connection
    
        // add code here.
    }
    

    在处理异步命令时(来自 gui 事件),不要忘记禁用控件以防止执行多个命令。

    【讨论】:

    • 谢谢,但是如何将 body 对象从 .On 传递到 UI?这些函数属于不同的类。
    • 从未使用过SignalR,但我假设每当消息到达时调用body 时已经返回Start() 方法。所以对于在哪里处理收到的消息存在误解。您无需在await hubConnection.Start() 之后从On&lt;Body&gt; 事件中“移动”对象。在那里处理消息。
    • 你是对的。但是类 ProtocolClient 没有也不应该与 UI 元素有任何关系。
    【解决方案2】:

    不要使用async void 方法。如果不需要返回值,请使用async Task - 如果需要,请使用async Task&lt;SomeType&gt;

    然后,当您需要调用异步方法时(按照惯例,这些方法应该命名为ConnectAsyncSendTouchAsync),await 它:

    await SendTouchAsync(...);
    

    当异步工作流结束时,您的延续将被编组回 UI 线程(因为您在同步上下文中 awaited),您可以轻松地操作 UI。

    await 在您使用 async void 时似乎可以工作,但问题是调用者无法跟踪异步工作流 - 就调用者而言,该方法刚刚结束当时和现在,调用者中的代码照常继续。

    确保也将Canvas_PointerPressed 标记为async - 遗憾的是,在这种情况下,它必须async void。确保永远不要直接调用事件处理程序 - UI 线程可以正确处理回调,而您的代码不能。如果您需要与其他方法相同的逻辑,只需将其分成适当的async Task 方法和来自事件处理程序的await

    【讨论】:

    • 谢谢,但我仍然不知道如何以某种方式从 On 获取 body 对象到调用者类 MainPage。
    • @pythonimus 使用返回值即可。将签名更改为async Task&lt;Body&gt; SendTouchAsync(...),然后在里面做return body;await SendTouchAsync(...); 有你的返回值。
    • 谢谢,但我的问题是如何将 body 对象从 On function 获取到 UI。这部分:protocolHubProxy.On&lt;Body&gt;("BodiesChanged", body =&gt; //call a callback to return body);如何创建这个Action回调并传递给connect函数,让回调可以修改UI元素?
    • @pythonimus 啊,好的。在这种情况下,这是非常常见的“如何从另一个线程修改 UI”问题的 100% 重复 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-29
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    • 2015-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多