Akavache 的 GetObject<T> 在等待时挂起。知道这里出了什么问题吗?

Akavache's GetObject<T> hangs when awaited. Any idea what is wrong here?

我有一个 Xamarin.Forms 应用程序,我的应用程序中有这段代码 class(是的,这只是一个演示问题的示例):

    public App()
    {
        BlobCache.ApplicationName = "MyApp";
        BlobCache.EnsureInitialized();


        // The root page of your application
        MainPage = GetMainPage();
    }

    public object BlockingGetExternalUser()
    {
        return GetExternalUser().Result;
    }

    private async Task<object> GetExternalUser()
    {
        try
        {
            return await BlobCache.LocalMachine.GetObject<object>("user");
        }
        catch (KeyNotFoundException)
        {
            return null;
        }
    }

密钥 "user" 不存在,因此我希望得到 KeyNotFoundException。但是我从来没有看到抛出这个异常。相反,它只是 "hangs" 而不是来自 await GetObject 调用的 returns。

我 运行 在我的 phone 上 Android 5.0.

有什么解决办法吗?我做错了什么吗?

更新:附带说明:与其立即尝试 GetObject,不如尝试检查缓存中是否确实存在密钥,然后才从缓存中检索它。但是,如果我没记错的话,除了像上面的示例那样调用 GetObject 并捕获异常之外,没有其他方法可以进行检查。对于只想知道某个项目是否存在的场景,这似乎并不理想。也许在 Akavache 中使用 "Exists()" 方法会更好?或者我可能遗漏了什么?

更新 2:将示例更改为不在构造函数中使用异步方法。只是为了证明那不是问题所在。

更新 3:从构造函数中删除调用。当我从代码中的任何位置调用 BlockingGetExternalUser 时,等待仍会挂起。

在构造函数中使用异步方法 (var externalUser = GetExternalUser().Result;) 被认为是错误的代码。您不应在 class 构造函数中使用异步方法。读这个:Can constructors be async?

您可以尝试更改它以避免死锁:

Func<Task> task = async () => { await GetExternalUser().ConfigureAwait(false); };
task().Wait();

...但我不会推荐它。

您肯定遇到了死锁。引用 Synchronously waiting for an async operation, and why does Wait() freeze the program here

The await inside your asynchronous method is trying to come back to the UI thread.

Since the UI thread is busy waiting for the entire task to complete, you have a > deadlock.

请注意,您对 .Result 的调用在某处暗示了 Task.Wait()

有两种解决方案:要么完全避免使用 async 方法,要么将您的代码包装成 Task.Run,如下所示:

public object BlockingGetExternalUser()
{
    return Task.Run<object>(() => GetExternalUser().Result);
}

(我希望它正在编译我没有在 VS 中验证:)

根据经验,这些天我倾向于避免将 async 方法与 SQLite 结合使用。原因是大多数 SQLite 包装器库使用 Task.Run 反模式来提供 async 包装器围绕它们的方法,而 SQLite 没有任何内在的异步符号。请注意,将事物包装到 Task.Run 中以使其异步是完全没问题的,这只是库设计者的反模式,向他们的用户建议方法是异步的,而实际上它们不是。您可以在此处阅读更多相关信息: