문제

WebClient와 같은 일부 API는 사용합니다 이벤트 기반 비동기 패턴. 이것은 단순 해 보이고 느슨하게 결합 된 앱 (예 : UI의 배경 작업자)에서 잘 작동하지만 잘 어울리지는 않습니다.

예를 들어, 다중 스레드가있는 프로그램이 있으므로 비동기 작업이 차단되지 않습니다. (이것이 서버 앱에 들어가서 수백 번 전화를한다고 상상해보십시오. 스레드 풀 스레드를 차단하고 싶지 않습니다.) 우리는 3 개의 로컬 변수 ( "State")를 얻은 다음 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에 대해 자세한 확장 방법을 작성할 필요는 없습니까?) 시작 XXX와 ENDXXX는 쉽게 만들 수 있지만이 이벤트 방식은 어떤 식 으로든 제공하지 않는 것 같습니다.

도움이 되었습니까?

해결책

당신은 조사하고 싶을 수도 있습니다 F#. F# «Workflow»기능 으로이 코딩을 자동화 할 수 있습니다. '08 PDC 프레젠테이션 F# 불리는 표준 라이브러리 워크 플로우를 사용하여 비동기 웹 요청을 처리합니다. async,이를 처리합니다 BeginXXX/EndXXX 패턴이지만 많은 어려움없이 이벤트 패턴에 대한 워크 플로우를 작성하거나 통조림을 찾을 수 있습니다. 그리고 f#은 C#과 잘 작동합니다.

다른 팁

과거에는 반복자 메소드를 사용 하여이 구현을 구현했습니다. 다른 URL을 요청할 때마다 "수율 리턴"을 사용하여 제어를 기본 프로그램으로 다시 전달합니다. 요청이 완료되면 기본 프로그램은 반복자로 다시 호출하여 다음 작업을 실행합니다.

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();
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top