【问题标题】:why does async call LiveConnectClient.GetAsync block executing thread?为什么异步调用 LiveConnectClient.GetAsync 块执行线程?
【发布时间】:2014-04-13 19:08:54
【问题描述】:

我有 Windows Store MonoGame(基于 Visual Studio 2012 中的 XAML MonoGame 模板)应用程序。
当我连接到 LiveConnect 时,系统会在后台执行所有操作,但是当我调用 LiveConnectClient.GetAsync 来获取用户信息时,它有时(并且通常)会阻塞调用者线程,即使它是使用 await 调用的。 有没有办法让 GetAsync 调用真正异步?也许我应该创建一个新线程来调用它?

这是调用者代码。它在 MonoGame 绘制线程中调用(在 MonoGame 中无法访问主 UI 线程)。

private static LiveConnectSession session = null;
private static LiveAuthClient liveAuthClient = null;
private static LiveConnectClient liveConnectClient = null;

public static async Task AuthAsync()
{
    liveAuthClient = new LiveAuthClient();
    LiveLoginResult liveLoginResult = await liveAuthClient.InitializeAsync();
    liveLoginResult = await liveAuthClient.LoginAsync(new List<string> { "wl.signin" });
    if (liveLoginResult.Status == LiveConnectSessionStatus.Connected)
    {
        session = liveLoginResult.Session;
        liveConnectClient = new LiveConnectClient(session);
        LiveOperationResult liveOperationResult = await liveConnectClient.GetAsync("me");
        dynamic meResult = liveOperationResult.Result;
        MyEngine.userID = meResult.id;
    }
}

【问题讨论】:

  • 你必须使用调用AuthAsync的方法。如果你打电话给AuthAsync.Wait(),就可以了。
  • 我在没有 Wait() 或 await 的情况下调用它(我知道我可以丢失异常,但在这种情况下不在乎)。所以我只是开火并继续线程执行。我的堆栈(非主线程):Monogame 的 void Game.Update-> void MyEngine.Update -> async void CurrentGameLevel.Update-> async Task AuthAsync。如果我评论“等待 liveConnectClient.GetAsync” - 一切顺利,否则游戏停止长达 10 秒 - 就像我使用同步调用一样。
  • 根据this,您可能想尝试将其包装在Task.Run 中。我认为他在那里所做的基本上是将AuthAsync 变成async void 方法,或者只是强行将其移动到另一个线程。另一种选择是做类似的事情,但使用TaskCompletionSourceAuthAsync 中返回Task,并让被调用的async 方法设置Task 的结果。
  • 已尝试 Task.Run,​​但看起来 LiveConnect 无法登录此类 bg 线程: throws InnerException {"Unsupported. (Exception from HRESULT: 0x80070032)"} System.Exception。在 WP8 中,我使用 Deployment.Current.Dispatcher.BeginInvoke 在主线程中启动类似的 LiveConnect 代码,并且没有选项:只有主线程。尽管有所有这些等待,但同样的冻结。
  • 我想我知道发生了什么。我认为它正在检查用户是否使用 Microsoft 帐户登录,因为如果不是,则需要抛出一个登录对话框。这可能就是它需要 UI 线程的原因。

标签: c# windows-store-apps async-await monogame live-connect-sdk


【解决方案1】:

有点离题(不是关于线程),但可能对某人有帮助。

我发现调用 LiveAuthClient 的 LoginAsync() 需要最多 12 秒,通常大约需要 7-8 秒(在 win 8.1 商店应用中)在 c# 和 XAML 中)

例如

LiveAuthClient auth = new LiveAuthClient(_redirectDomain);
_loginResult = await auth.LoginAsync(new string[] { "wl.signin", "wl.basic", "wl.emails" });

发现一旦我关闭了 Fiddler(一个 http 代理/检查器),这下降到一个更更合理的 ~2 秒

仍然很慢而且很蹩脚(不知道为什么这么慢 - 注意:我认为从 5.5 升级到 5.6 会减慢它的速度)但显然要好得多。

可能会帮助某人。

【讨论】:

    【解决方案2】:

    感谢 Nate Diamond,我找到了一种解决方法(或者也许这是唯一的解决方案)。诀窍是等待初始化并在主线程中连接(在Windows商店应用程序中,它不是ui线程,但不知何故它是主线程),然后创建线程并在其中等待GetAsync。为了清楚起见,我跳过了所有 try..catch..finally 和所有不必要的内容。现在它让绘制线程工作没有冻结。这是代码:

    private static LiveConnectSession session = null;
    private static LiveAuthClient liveAuthClient = null;
    private static LiveConnectClient liveConnectClient = null;
    
    public static async Task AuthAsync()
    {
        await AuthAsyncInternal();
        if (liveConnectClient != null)
        {
            await Task.Run(async () =>
                {
                    LiveOperationResult liveOperationResult = 
                        await liveConnectClient.("me");
                    dynamic meResult = liveOperationResult.Result;
                    MyEngine.userID = meResult.id;
                });
        }
    }
    
    private static async Task AuthAsyncInternal()
    {
        liveAuthClient = new LiveAuthClient();
        LiveLoginResult liveLoginResult = await liveAuthClient.InitializeAsync();
        liveLoginResult = await liveAuthClient.LoginAsync(new List<string> { "wl.signin" });
        if (liveLoginResult.Status == LiveConnectSessionStatus.Connected)
        {
            session = liveLoginResult.Session;
            liveConnectClient = new LiveConnectClient(session);
        }
    }
    

    这是适用于 Windows Phone 8 的变体:

    private static async Task AuthAsyncInternal()
    {
        Deployment.Current.Dispatcher.BeginInvoke(async delegate()
            {
                liveAuthClient = new LiveAuthClient("your client id here");
                LiveLoginResult liveLoginResult = await liveAuthClient.InitializeAsync();
                liveLoginResult = await liveAuthClient.LoginAsync(new List<string> { "wl.signin" });
                if (liveLoginResult.Status == LiveConnectSessionStatus.Connected)
                {
                    session = liveLoginResult.Session;
                    liveConnectClient = new LiveConnectClient(session);
                    await Task.Run(async () =>
                        {
                            LiveOperationResult liveOperationResult = 
                                await liveConnectClient.("me");
                            dynamic meResult = liveOperationResult.Result;
                            MyEngine.userID = meResult.id;
                        });
                }
            });
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-27
      相关资源
      最近更新 更多