Akavache GetObject <T> зависает при ожидании. Любая идея, что здесь не так?

У меня есть приложение Xamarin.Forms, с этим кодом в моем классе App (да, это всего лишь образец, чтобы продемонстрировать проблему):

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

Ключ «пользователь» не существует, поэтому я ожидаю получить исключение KeyNotFoundException. Однако я никогда не вижу, чтобы это исключение было брошено. Вместо этого он просто «зависает» и никогда не возвращается с ожидающего вызова GetObject.

Я запускаю это на своем телефоне с Android 5.0.

Любые идеи, как это исправить? Я что-то принципиально неправильно делаю?

Обновление: на стороне примечания: вместо того, чтобы немедленно попробовать GetObject, можно попытаться проверить, действительно ли ключ существует в кеше и только затем извлечь его из кеша. Однако, если я не ошибаюсь, нет другого способа сделать проверку, кроме вызова GetObject и перехвата исключения, как в примере выше. Для сценария, где нужно просто знать, существует ли элемент, который не кажется идеальным. Может быть, метод «Exists ()» был бы приятным в Akavache? Или, может, я чего-то не хватает?

Update2: изменение примера, чтобы не использовать метод async в конструкторе. Просто чтобы доказать, что это не проблема.

Update3: Удаление вызова из конструктора. Когда я вызываю BlockingGetExternalUser из любого места в моем коде, ожидание все равно будет зависать.

У тебя наверняка есть мертвый замок. Цитирование Синхронно ожидание операции async и почему Wait () заморозит программу здесь :

Ожидание внутри вашего асинхронного метода пытается вернуться к потоку пользовательского интерфейса.

Поскольку поток пользовательского интерфейса занят, ожидая завершения всей задачи, у вас есть «тупик».

Обратите внимание, что ваш вызов .Result означает Task.Wait() где-то.

Существует два решения: либо полностью избежать async методов, либо обернуть свой код в Task.Run как это:

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

(Я надеюсь, что это компиляция, я не проверял в VS 🙂

По опыту я стараюсь избегать async методов в сочетании с SQLite в наши дни. Причина в том, что большинство библиотек- Task.Run SQLite используют Anti-pattern Task.Run для предоставления оберток async вокруг своих методов, в то время как SQLite не имеет каких-либо внутренних обозначений асинхронности. Обратите внимание, что для вас идеально подходит для Task.Run вещей в Task.Run чтобы сделать их асинхронными и что это только анти-шаблон для дизайнеров библиотек, предлагая своим пользователям, что методы асинхронны, когда они на самом деле нет. Подробнее об этом вы можете прочитать здесь: Задача. Запустить как анти-шаблон?

Использование async-методов в конструкторах ( var externalUser = GetExternalUser().Result; ) считается плохим кодом. Вы не должны использовать методы async в конструкторах классов. Прочтите: могут ли конструкторы быть асинхронными?

Вы можете попытаться изменить его, чтобы избежать взаимоблокировок:

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

… но я не буду рекомендовать его.