【发布时间】:2017-06-10 11:18:31
【问题描述】:
我正在 Xamarin iOS 应用程序中编写一些基本的 Firebase 代码,并且遇到了 TaskCompletionSource 的经典死锁情况。
public Task<string> GetUsers()
{
var tcs = new TaskCompletionSource<string>();
_instance.GetChild("users").ObserveSingleEvent(DataEventType.Value,
x => { tcs.SetResult(x); });
return tcs.Task;
}
当我像这样阻止这段代码时:
var users = GetUsers().Result;
应用程序死锁。
如果我理解正确,回调会尝试在 .Result 等待的同一上下文中运行。
我不明白的是,如果我修改代码以等待 Task 中的 GetUsers() 调用,如下所示:
var result = Task.Run(
async () => await AppContext.Database.GetUsers().ConfigureAwait(false)
).Result;
它仍然死锁。
第二种情况是怎么回事?由于Task.Run,代码在另一个线程上运行的事实不应该意味着外部.Result不会阻止回调调用吗?
编辑:
跟进 Nkosi 的评论,我之所以问这个问题,是因为我很好奇为什么代码会阻塞。如果我等待电话
var users = await GetUsers().ConfigureAwait(false);
然后僵局就会消失。我只是想了解为什么它在包裹在Task 中时会阻塞,因为根据我对Task.Run 的(显然不正确的)理解,它不应该。
【问题讨论】:
-
对我来说这似乎是一个 XY 问题。在minimal reproducible example 中显示您最终要实现的目标,并且可能是可以解决的解决方案。它看起来像是异步调用和阻塞调用的混合。所以上面的调用堆栈也应该显示出来。
-
@Nkosi 我实际上并没有被卡住,因为如果我只是做
var users = await Getusers().ConfigureAwait(false)那么代码运行时不会出现死锁。我只是好奇为什么Task.Run版本不起作用,因为根据我的理解它应该。 -
好的。我对你的问题的误解。你熟悉 Stephen Cleary 的这篇文章吗msdn.microsoft.com/en-us/magazine/jj991977.aspx
-
@Nkosi 另一个 SO 问题真的很有趣——他描述的“线程池黑客”正是我 认为 我正在做的事情:将异步操作放在另一个线程。开始怀疑这是否与 Xamarin 相关... 将更深入地研究他在该问题 (msdn.microsoft.com/en-us/magazine/mt238404.aspx) 中引用的文章,看看我是否能在其中找到答案。
标签: c# firebase asynchronous xamarin.ios synchronizationcontext