使用同步 UI 线程的回调调用异步方法

本文关键字:回调 调用 异步方法 线程 同步 UI | 更新日期: 2023-09-27 17:56:23

我真的被困在这里了...我有一个 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 并向他们传递回调?

使用同步 UI 线程的回调调用异步方法

你不需要回调。只需在 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 事件)时,不要忘记禁用控件以防止多次执行命令。

不要使用async void方法。如果不需要返回值,请使用 async Task - 如果返回,请使用 async Task<SomeType>

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

await SendTouchAsync(...);

异步工作流结束时,您的延续将被封送回 UI 线程(因为您从同步上下文中await ed),并且您可以轻松地操作 UI。

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

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