我正在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
等待的相同上下文中运行。
我不明白的是,如果我修改我的代码以等待GetUsers()
Task
中的var result = Task.Run(
async () => await AppContext.Database.GetUsers().ConfigureAwait(false)
).Result;
调用,那么:
Task.Run
它仍然陷入僵局。
第二种情况发生了什么?不应该由于.Result
导致代码在另一个线程上运行,这意味着外部var users = await GetUsers().ConfigureAwait(false);
不会阻止回调调用吗?
修改
跟进Nkosi的评论我问这个是因为我很好奇代码阻塞的原因。如果我等待电话
Task
然后僵局就消失了。我只是想了解为什么它被包裹在Task.Run
时会阻止,因为根据我对{{1}}的理解(明显不正确),它不应该。
答案 0 :(得分:3)
ObserveSingleEvent
总是调度回调到UI线程(我认为所有或几乎所有的firebase回调都会这样做)。它不捕获同步上下文或类似的东西 - 只是总是调用回调到UI线程(记住 - 它只是本机IOS代码的包装)。因此,当您通过等待Result
来阻止您的UI线程时 - 无论您从哪个线程调用GetUsers
,它都会因显而易见的原因而死锁。你提到的链接描述了被叫代码捕获当前同步上下文时的另一种情况,因此它们从没有同步上下文的后台线程调用该代码,并且不会将回调发布到它。情况并非如此。