2

Xamarin iOSアプリに基本的なFirebaseコードを書き、古典的なデッドロック状況であるTaskCompletionSourceを実行しています。TaskCompletionSource SynchronizationContext

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が待機しているのと同じコンテキストで実行しようとしています。

私は理解していないが、私はそうのようなTaskGetUsers()コールを待つために自分のコードを変更した場合ということです:

var result = Task.Run(
    async() => await AppContext.Database.GetUsers().ConfigureAwait(false) 
).Result; 

それはまだデッドロック。

2番目のケースでは何が起こっていますか? Task.Runのためにコードが別のスレッドで実行されているということは、外部の.Resultがコールバック呼び出しをブロックしないことを意味しますか?

EDIT:

は、私は、コードをブロックしている理由として興味ので、私はこれを聞いてるのよNkosiさんのコメントフォローアップ。電話を待つ場合

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

デッドロックがなくなります。私はちょうどTaskに包まれたときにブロックする理由を理解したいと思います。これは私の(明らかに間違った)理解に基づいてTask.Runに基づいているので、そうしてはいけません。

+0

私にとってこれはXY問題のようです。最終的に[mcve]で達成しようとしていることを示し、解決策が解決できるかもしれません。これは、非同期呼び出しとブロック呼び出しの混在のように見えます。上のコールスタックも同様に表示する必要があります。 – Nkosi

+0

@Nkosi私はちょうど 'var users = Getusers()。ConfigureAwait(false)'を待っているとコードがデッドロックなしで実行されるので、実際には止まっていません。私は 'Task.Run'バージョンがなぜ私の理解に基づいて動作するのではないのか不思議です。 –

+1

あなたの質問に対する私の誤解。あなたはStephen Clearyのこの記事に精通していますかhttps://msdn.microsoft.com/en-us/magazine/jj991977.aspx – Nkosi

答えて

3

ObserveSingleEventは常にUIスレッドにコールバックを送ります(そして、すべてのまたはほとんどのfirebaseコールバックがそれを行うと思います)。シンクロナイズのコンテキストなどをキャプチャしません。ちょうど常にコールバックをUIスレッドに送ります(覚えておいてください。これはネイティブIOSコードのラッパーです)。したがって、Resultを待ってUIスレッドをブロックすると、どちらのスレッドからでもGetUsersに関係なく、デッドロックが発生します。あなたが言及したリンクは、コードと呼ばれる別の状況を記述しているので、現在のシンクロナイズ・コンテキストをキャプチャしているため、シンクロナイズ・コンテキストを持たないバックグラウンド・スレッドからそのコードを呼び出し、コールバックはそれにはポストされません。ここではそうではありません。

関連する問題