【问题标题】:Exception: The application called an interface that was marshalled for a different thread例外:应用程序调用了为不同线程编组的接口
【发布时间】:2013-06-22 08:42:38
【问题描述】:
private void LogInButton_Click(object sender, RoutedEventArgs e)
{
    var api = new RestAPI("http://localhost:2624/", UsernameTextBox.Text, PasswordTextBox.Password);

    api.AutenticarUsuarioFinalizado += (o, args) =>
    {
        ProgressBar.IsIndeterminate = false;
        ProgressBar.Visibility = Visibility.Collapsed;
        LogInButton.IsEnabled = true;

        if (args.Error) return;

        if (args.Resultado.Autenticado)
        {
        }
    };

    api.AutenticarUsuario();
    ProgressBar.Visibility = Visibility.Visible;
    ProgressBar.IsIndeterminate = true;
    LogInButton.IsEnabled = false;
}

api.AutenticarUsuario(); 异步调用 rest API,完成后它调用事件处理程序 api.AutenticarUsuarioFinalizado 并在 ProgressBar 行中收到此错误.IsIndeterminate = false; 因为调用打开了一个新线程,我该如何解决?错误是:

应用程序调用了一个为不同线程编组的接口。

【问题讨论】:

    标签: c# multithreading windows-store-apps async-await


    【解决方案1】:

    问题是您的事件处理程序没有在 UI 线程上执行。我认为解决此问题的最佳方法是使用TaskCompletionSource 将您的 EAP(基于事件的异步模式)方法转换为 TAP(基于任务的异步模式):

    public static Task<Resultado> AutenticarUsuarioAsync(this RestAPI api)
    {
        var tcs = new TaskCompletionSource<Resultado>();
    
        api.AutenticarUsuarioFinalizado += (sender, args) =>
        {
            if (args.Error)
                tcs.TrySetException(new SomeAppropriateException());
            else
                tcs.TrySetResult(args.Resultado);
        };
    
        api.AutenticarUsuario();
    
        return tcs.Task;
    }
    
    …
    
    private async void LogInButton_Click(object sender, RoutedEventArgs e)
    {
        var api = new RestAPI(
            "http://localhost:2624/", UsernameTextBox.Text,
            PasswordTextBox.Password);
    
        ProgressBar.Visibility = Visibility.Visible;
        ProgressBar.IsIndeterminate = true;
        LogInButton.IsEnabled = false;
    
        try
        {
            var resultado = await api.AutenticarUsuarioAsync();
            if (resultado.Autenticado)
            {
                // whatever
            }
        }
        catch (SomeAppropriateException ex)
        {
            // handle the exception here
        }
        finally
        {
            ProgressBar.IsIndeterminate = false;
            ProgressBar.Visibility = Visibility.Collapsed;
            LogInButton.IsEnabled = true;
        }
    }
    

    因为awaitTask 将始终在原始上下文中恢复,因此您不会以这种方式获得异常。另一个优势是,您不必像使用 EAP 那样“由内而外”编写代码。

    您还应该考虑使用绑定,而不是手动设置 UI 控件的属性。

    【讨论】:

    • 这是一个快速原型,这就是我没有使用绑定的原因,但我会 :) 谢谢
    【解决方案2】:

    您的 AutenticarUsuarioFinalizado 事件处理程序可能正在与 UI 线程不同的线程中执行。您应该只从 UI 线程修改/创建 UI 对象。在您的事件处理程序中使用调度程序,请在此处查看:Run code on UI thread in WinRT

    var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
    api.AutenticarUsuarioFinalizado += (o, args) =>
    {
       dispatcher.RunAsync(DispatcherPriority.Normal, () => 
           ProgressBar.IsIndeterminate = false;
           ProgressBar.Visibility = Visibility.Collapsed;
       [...]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-21
      • 1970-01-01
      • 1970-01-01
      • 2011-04-05
      • 2023-03-20
      • 1970-01-01
      • 2013-10-20
      相关资源
      最近更新 更多