任务完成源同步上下文



我正在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不会阻止回调调用吗?

编辑:

跟进 Nkosi 的评论,我问这个是因为我很好奇为什么代码被阻止。如果我在等待电话

var users = await GetUsers().ConfigureAwait(false);

然后僵局消失了。我只是想了解为什么它在包裹在Task中时会阻塞,因为根据我对Task.Run的(显然不正确的)理解,它不应该。

ObserveSingleEvent总是将回调调度到UI线程(我认为所有或几乎所有Firebase回调都这样做)。它不会捕获同步上下文或类似的东西 - 只是总是将回调调度到 UI 线程(记住 - 它只是本机 IOS 代码的包装器)。因此,当您通过等待Result来阻止 UI 线程时 - 无论您从哪个线程调用GetUsers,它都会因明显的原因而死锁。您提到的链接描述了调用代码捕获当前同步上下文时的另一种情况,因此它们从没有同步上下文的后台线程调用该代码,并且不会向其发布回调。这里的情况并非如此。

最新更新