该应用程序调用了一个为不同线程编组的接口 - Xamarin Forms

Posted

技术标签:

【中文标题】该应用程序调用了一个为不同线程编组的接口 - Xamarin Forms【英文标题】:The application called an interface that was marshalled for a different thread - Xamarin Forms 【发布时间】:2017-06-19 02:12:32 【问题描述】:

我在加载给定页面时通过 WCF 加载一些数据。然后,我想在数据加载后用值填充 Picker。但是,我遇到了跨线程问题。代码如下:

protected async override void OnAppearing()

    base.OnAppearing();
    await Task.Factory.StartNew(async () =>  await Initialise(); );


private async Task Initialise()

    var activities = await App.XivicServicesClient.DataAccessCalls.GetRecordsAsync<ActivityDef>(null, new ConnectionInfo(null, App.XivicServicesClient.AuthToken));

    var activityCodes = activities.Select(a => a.Code);
    foreach (var activityCode in activityCodes)
    
        ActivityPicker.Items.Add(activityCode);
    

在 Windows UWP 中,我得到的错误是

附加信息:应用程序调用了为不同线程编组的接口。 (来自 HRESULT 的异常:0x8001010E (RPC_E_WRONG_THREAD))

我想我正在以一种意想不到的方式使用 OnAppearing 调用,那么我应该如何解决这个问题?我需要在页面开始加载时进行基于任务的异步 WCF 调用。

ios 上,我没有收到错误消息。

【问题讨论】:

如前所述,您不应在此处使用Task.Factory.StartNew。更何况Task.Factory.StartNew是一种危险的方法;即使您想要在后台线程上运行代码,也几乎不应该使用它(改用Task.Run)。 【参考方案1】:

你的问题来自于

ActivityPicker.Item.Add(activityCode);

该行只能在 UI 线程上运行。

您可以使用以下方法轻松修复此代码

protected async override void OnAppearing()

    base.OnAppearing();
    var activityCodes = await Task.Factory.StartNew(async () =>  await Initialise(); );

    foreach (var activityCode in activityCodes)
    
        ActivityPicker.Items.Add(activityCode);
    


private async Task<List<string>> Initialise()

    var activities = await App.XivicServicesClient
                       .DataAccessCalls
                       .GetRecordsAsync<ActivityDef>(null, new ConnectionInfo(null, App.XivicServicesClient.AuthToken));

    return activities.Select(a => a.Code).ToList();


但是,我真的怀疑您“需要”使用Task.Factory.StartNewGetRecordsAsync 已经是一个异步方法。 Initialise(sic) 中的大部分延迟可能来自 I/O 延迟。

从另一个线程运行 Initialise 只会增加运行时间,因为数据需要跨线程封送以及同步。

不如试试吧。

protected async override void OnAppearing()

    base.OnAppearing();

    var activities = await App.XivicServicesClient
                          .DataAccessCalls
                          .GetRecordsAsync<ActivityDef>(null, new ConnectionInfo(null, App.XivicServicesClient.AuthToken));

    foreach (var activityCode in activities.Select(a => a.Code))
    
        ActivityPicker.Items.Add(activityCode);
    

【讨论】:

这里有很多很棒的信息。您对我从反复试验中得出的结论给出了很好的解释。【参考方案2】:

问题很简单。我添加了不必要的代码,而该代码导致了该错误。下面是固定方法:

protected async override void OnAppearing()

    base.OnAppearing();
    await Initialise();

【讨论】:

以上是关于该应用程序调用了一个为不同线程编组的接口 - Xamarin Forms的主要内容,如果未能解决你的问题,请参考以下文章

应用程序调用了为不同线程编组的接口 - Windows Store App

为 COM 接口启用编组需要啥?

通过非默认 AppDomain 中的 C# 函数调用编组 C++ 指针接口

创建 COM 接口,返回一个在 C# 中编组为 IntPtr 的指针

将 COM 接口编组到多个线程时,克隆流是不是足够,还是需要复制?

如何手动将 .NET 对象编组为双 COM 接口?