2

次のようなコードがあります。

abstract class Data
{
    Data(string name, bool load) { if (load) { Load().Wait(); }
    abstract Task Load();
}

class XmlData : Data
{
    XmlData(string name, bool load = true) : base(name, load) {}
    override async Task Load()
    {
        var file = await GetFileAsync(...);
        var xmlDoc = await LoadXmlDocAsync(file);
        ProcessXml(xmlDoc);
    }
    void ProcessXml(XmlDocument xmlDoc)
    {
        foreach (var element in xmlDoc.Nodes)
        {
            if (element.NodeName == "something")
                new XmlData(element.NodeText);
        }
    }
}

GetFileAsync(...) でコードがハングしてしまうという奇妙なタイミングの問題が (時々) 発生するようです。これは、呼び出しの再帰的な性質が原因ですか? すべての await 呼び出しを実際に .Wait() を実行して終了するように変更し、本質的に呼び出しのすべての非同期性を取り除くと、私のコードは正常に実行されます。

4

1 に答える 1

3

これは、呼び出しの再帰的な性質が原因ですか? すべての await 呼び出しを実際に .Wait() を実行して終了するように変更し、本質的に呼び出しのすべての非同期性を取り除くと、私のコードは正常に実行されます。

それは本当に依存します-

最も可能性の高い原因は、呼び出し元がユーザー インターフェイス スレッドを何らかの方法でブロックしている場合です (Wait() の呼び出しなどを介して)。この場合、await のデフォルトの動作は、呼び出し元の同期コンテキストをキャプチャし、結果をそのコンテキストにポストすることです。

ただし、呼び出し元がそのコンテキストを使用している場合、デッドロックが発生する可能性があります。

これは非常に可能性の高いケースであり、次のコード行が原因です。

Data(string name, bool load) { if (load) { Load.Wait(); }

これは、ライブラリ コード (この XmlData クラスなど)で呼び出し元の同期コンテキストを明示的に使用しないようにすることで簡単に回避できます。これは通常、ユーザー インターフェイス コードにのみ必要です。キャプチャを回避することで、2 つのことを行います。第一に、全体的なパフォーマンスを (多くの場合劇的に) 向上させ、第二に、このデッドロック状態を回避します。

これは、ConfigureAwaitを使用してコードを次のように変更することで実行できます。

override async Task Load()
{
    var file = await GetFileAsync.(...).ConfigureAwait(false);
    var xmlDoc = await LoadXmlDocAsync(file).ConfigureAwait(false);
    ProcessXml(xmlDoc);
}

そうは言っても、このデザインを少し考え直します。ここには実際には 2 つの問題があります。

まず、コンストラクターに仮想メソッド呼び出しを配置し​​ます。これはかなり危険であり、異常な問題が発生する可能性があるため、可能な限り避ける必要があります。

次に、これをブロックとともにコンストラクターに配置することにより、非同期操作全体を同期操作に変換します。代わりに、この全体を再考することをお勧めします。

おそらく、これを作り直して、非同期にロードされたデータを返す何らかの形式のファクトリを作成できますか? これは、作成用のパブリック API を を返すファクトリ メソッドにするTask<Data>か、ジェネリックpublic async Task<TData> Create<TData>(string name) where TData : Dataメソッドにするだけで簡単に作成できます。これにより、構築と読み込みを非同期に保ち、ブロックを完全に回避できます。

于 2012-03-06T19:31:14.940 に答える