代码之家  ›  专栏  ›  技术社区  ›  MichaelGG

C中基于事件的异步;有没有可能进行一般的重构?

  •  2
  • MichaelGG  · 技术社区  · 16 年前

    一些API,如WebClient,使用 Event-based Async pattern . 虽然这看起来很简单,而且可能在松散耦合的应用程序(比如,UI中的后台工作人员)中很好地工作,但它并不能很好地连接在一起。

    例如,这里有一个多线程的程序,这样异步工作就不会阻塞。(假设这是在一个服务器应用程序中进行的,调用了数百次——你不想阻塞线程池线程。)我们得到3个局部变量(“状态”),然后进行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让事情变得简单,但这种活动方式似乎没有提供任何方式。

    2 回复  |  直到 15 年前
        1
  •  1
  •   Anton Tykhyy    15 年前

    你可能想调查一下 F# . 弗斯 可以使用“工作流”功能为您自动化此编码。08年的PDC演示 弗斯 使用标准库工作流处理异步Web请求 async ,处理 BeginXXX / EndXXX 模式,但是您可以为事件模式编写一个工作流而不需要太多的困难,或者找到一个封闭的工作流。F和C很好地配合。

        2
  •  4
  •   Tim Robinson    16 年前

    在过去,我使用迭代器方法实现了这一点:每次您想要请求另一个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();
        }
    }