2

WebClient などの一部の API は、イベント ベースの非同期パターンを使用します。これは単純に見え、疎結合のアプリ (UI の BackgroundWorker など) ではうまく機能する可能性がありますが、連鎖はあまりうまくいきません。

たとえば、非同期処理がブロックされないようにマルチスレッド化されたプログラムを次に示します。(これがサーバー アプリで行われ、何百回も呼び出されると想像してください。ThreadPool スレッドをブロックしたくありません。) 3 つのローカル変数 (「状態」) を取得し、2 つの非同期呼び出しを行い、その結果を最初に2番目のリクエストにフィードします(したがって、それらは並行して進むことができません)。状態も変化する可能性があります (簡単に追加できます)。

WebClient を使用すると、次のようになります (または、クロージャーのように動作するオブジェクトの束を作成することになります)。

using System;
using System.Net;

class Program
{
    static void onEx(Exception ex) {
        Console.WriteLine(ex.ToString());
    }

    static void Main() {
        var url1 = new Uri(Console.ReadLine());
        var url2 = new Uri(Console.ReadLine());
        var someData = Console.ReadLine();

        var webThingy = new WebClient();
        DownloadDataCompletedEventHandler first = null;
        webThingy.DownloadDataCompleted += first = (o, res1) => {
            if (res1.Error != null) {
                onEx(res1.Error);
                return;
            }
            webThingy.DownloadDataCompleted -= first;
            webThingy.DownloadDataCompleted += (o2, res2) => {
                if (res2.Error != null) {
                    onEx(res2.Error);
                    return;
                }
                try {
                    Console.WriteLine(someData + res2.Result);
                } catch (Exception ex) { onEx(ex); }
            };
            try {
                webThingy.DownloadDataAsync(new Uri(url2.ToString() + "?data=" + res1.Result));
            } catch (Exception ex) { onEx(ex); }
        };
        try {
            webThingy.DownloadDataAsync(url1);
        } catch (Exception ex) { onEx(ex); }

        Console.WriteLine("Keeping process alive");
        Console.ReadLine();
    }

}

このイベントベースの非同期パターンをリファクタリングする一般的な方法はありますか? (つまり、このような API ごとに詳細な拡張メソッドを記述する必要はありませんか?) BeginXXX と EndXXX を使用すると簡単ですが、このイベントの方法では方法がないようです。

4

2 に答える 2

4

過去に、反復子メソッドを使用してこれを実装しました。別の URL を要求するたびに、「yield return」を使用して制御をメイン プログラムに戻します。リクエストが完了すると、メイン プログラムはイテレータにコールバックして、次の作業を実行します。

C# コンパイラを効果的に使用して、ステート マシンを記述しています。利点は、反復子メソッドで通常の外観の C# コードを記述して、全体を駆動できることです。

using System;
using System.Collections.Generic;
using System.Net;

class Program
{
    static void onEx(Exception ex) {
        Console.WriteLine(ex.ToString());
    }

    static IEnumerable<Uri> Downloader(Func<DownloadDataCompletedEventArgs> getLastResult) {
        Uri url1 = new Uri(Console.ReadLine());
        Uri url2 = new Uri(Console.ReadLine());
        string someData = Console.ReadLine();
        yield return url1;

        DownloadDataCompletedEventArgs res1 = getLastResult();
        yield return new Uri(url2.ToString() + "?data=" + res1.Result);

        DownloadDataCompletedEventArgs res2 = getLastResult();
        Console.WriteLine(someData + res2.Result);
    }

    static void StartNextRequest(WebClient webThingy, IEnumerator<Uri> enumerator) {
        if (enumerator.MoveNext()) {
            Uri uri = enumerator.Current;

            try {
                Console.WriteLine("Requesting {0}", uri);
                webThingy.DownloadDataAsync(uri);
            } catch (Exception ex) { onEx(ex); }
        }
        else
            Console.WriteLine("Finished");
    }

    static void Main() {
        DownloadDataCompletedEventArgs lastResult = null;
        Func<DownloadDataCompletedEventArgs> getLastResult = delegate { return lastResult; };
        IEnumerable<Uri> enumerable = Downloader(getLastResult);
        using (IEnumerator<Uri> enumerator = enumerable.GetEnumerator())
        {
            WebClient webThingy = new WebClient();
            webThingy.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e) {
                if (e.Error == null) {
                    lastResult = e;
                    StartNextRequest(webThingy, enumerator);
                }
                else
                    onEx(e.Error);
            };

            StartNextRequest(webThingy, enumerator);
        }

        Console.WriteLine("Keeping process alive");
        Console.ReadLine();
    }
}
于 2008-10-29T10:13:04.657 に答える
1

を調べてみてくださいF#F#«ワークフロー»機能を使用して、このコーディングを自動化できます。'08 PDCプレゼンテーションは、 /パターンを処理する、F#と呼ばれる標準ライブラリワークフローを使用して非同期Webリクエストを処理しましたが、イベントパターンのワークフローを簡単に作成することも、既定のワークフローを見つけることもできます。また、F#はC#でうまく機能します。asyncBeginXXXEndXXX

于 2009-03-13T16:23:24.717 に答える