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

假装没事ソ 提交于 2020-01-23 08:11:29

问题


I have a Xamarin.Forms application, with this code in my App class (yes, this is just a sample to demonstrate the issue):

    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;
        }
    }

The Key "user" does not exist, so I would expect to get a KeyNotFoundException. However I never see this exception being thrown. Instead it just "hangs" and never returns from the await GetObject call.

I am running this on my phone with Android 5.0.

Any ideas how to fix this? Am I doing something fundamentally wrong?

Update: On a side note: Instead of immediately trying GetObject, one could try to check if the key actually exists in the cache and only then retrieve it from the cache. However, if I am not mistaken, there is no other way to do a check other than calling GetObject and catching the exception like in the sample above. For a scenario where one would just want to know if an item exists, that doesn't seem to be ideal. Maybe an "Exists()" method would be a nice to have in Akavache? Or maybe I am missing something?

Update2: Changing the example to not use an async method in the constructor. Just to prove a point that that is not the issue.

Update3: Removing the call from the constructor. When I call BlockingGetExternalUser from anywhere in my code, the await will still hang.


回答1:


You're most certainly having a dead-lock. Quoting 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.

Note that your call to .Result implies a Task.Wait() somewhere.

There are two solutions: Either completely avoid the async methods, or wrap your code into a Task.Run like this:

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

(I hope it's compiling I didn't verify in VS :)

By experience I tend to avoid async methods in combination with SQLite these days. Reason is that most SQLite wrapper libraries use the Task.Run anti-pattern to provide async wrappers around their methods while SQLite doesn't have any intrinsic notations of being asynchronous. Note though that it's perfectly fine for you to wrap things into Task.Run to make them asynchronous and that it's only an anti-pattern for library designers, suggesting to their users that methods are asynchronous when they're actually not. You can read more about this here: Task.Run as an anti-pattern?




回答2:


Using async methods in a constructors (var externalUser = GetExternalUser().Result;) is considered as a bad code. You shouldn't use async methods in a class constructors. Read this: Can constructors be async?

You could try to change it to avoid deadlocks:

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

... but I won't recommend it.



来源:https://stackoverflow.com/questions/31425210/akavaches-getobjectt-hangs-when-awaited-any-idea-what-is-wrong-here

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!