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
中以使其异步是完全没问题的,这只是库设计者的反模式,向他们的用户建议方法是异步的,而实际上它们不是。您可以在此处阅读更多相关信息:
我有一个 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
中以使其异步是完全没问题的,这只是库设计者的反模式,向他们的用户建议方法是异步的,而实际上它们不是。您可以在此处阅读更多相关信息: