TaskCompletionSource SynchronizationContext

时间:2017-06-10 11:18:32

标签: c# firebase asynchronous xamarin.ios synchronizationcontext

我正在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}}的理解(明显不正确),它不应该。

1 个答案:

答案 0 :(得分:3)

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