【问题标题】:C#: how to implement as "async" and "!" in F#?C#:如何实现为“异步”和“!”在 F# 中?
【发布时间】:2011-08-01 03:26:46
【问题描述】:

最近读F#,原来是“异步”和“!” (砰!:p)是非常有用的功能。 (题外话,这是一个例子,说明编程语言不仅是一个学术研究课题,而且是一个现实生活中的生产力设计课题。

以下内容来自 Luca Bolognese 的 PDC 2008 演示文稿:

let internal loadPrices ticker = async {
    let url = "http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=9&e=30&f=2008&g=d&a=2&b=13&c=1986&ignore=.csv"
    let req = WebRequest.Create(url)
    let! resp = req.AsyncGetResponse() //athos: need F# Powerpack

    let stream = resp.GetResponseStream()
    let reader = new StreamReader(stream)
    let! csv = reader.AsyncReadToEnd()

    let prices =
        csv.Split([|'\n'|])
        |> Seq.skip 1
        |> Seq.map (fun line -> line.Split([|','|]))
        |> Seq.filter (fun values -> values |> Seq.length = 7)
        |> Seq.map (fun values ->
        System.DateTime.Parse(values.[0]),
        float values.[6])    

    return prices }

迷人之处在于,回调被优雅地处理:程序员不需要处理 BackgroundWorker 或 Theads 的 Join()。

那么,在 C# 中,是否有一个优雅的解决方案来实现 F#“aync”和“!”特征? 例如,是否可以像下面这样或以类似的轻松方式执行“async...run...callback”?

public void GetApplicationSettings()
{ 
    async(objAsyncRunner = new System.Async.Runner() ) //similar to "using" in C#
    run // similar to "if" in C#
    {
        string url = "http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=9&e=30&f=2008&g=d&a=2&b=13&c=1986&ignore=.csv"
        var req = WebRequest.Create(url);
        var resp = req.AsyncGetResponse();
    }
    callback run // similar to "if else" in C#
    {
        var stream = resp.GetResponseStream();
        var reader = new StreamReader(stream);
        var csv = reader.AsyncReadToEnd();
    }
    callback // similar to "else" in C#
    {
        var prices = csv.Split('\n').....;
    }
}

ps。我知道有些人会说:athos,就用 F#! 是的……我不反对F#,但整个部门都需要时间来改变,在此之前我希望能借鉴F#的一些优点。

我知道有一些 Aspect 编程概念可以扩展 C# 功能。但是还没有达到F#级别。

例如,我可以从 http://www.sharpcrafters.com 扩展 PostSharp,以便通过简单地将属性分配给方法,强制它在专用线程中异步运行:

[RunInADedicatedThread(Async = true)]
public void GetApplicationSettings()
{ ... }

如果属性已准备好:

[Serializable]
[AttributeUsage( AttributeTargets.Method | AttributeTargets.Class )]
[MulticastAttributeUsage( MulticastTargets.Method )]
public sealed class RunInADedicatedThreadAttribute : MethodInterceptionAspect
// MethodInterceptionAspect is extending PostSharp
{
    public bool Async { get; set; }

    public override void OnInvoke( MethodInterceptionArgs args )
    {
        if (Async)
        {
            //MyThreadPool is my extention for PostSharp 
            MyThreadPool.GetThread().ExecuteAsync(
                () =>
                    {
                        args.Proceed();
                    } );
            return;
        }
        MyThreadPool.GetThread().Execute( args.Proceed );
    }
}

这有点帮助,但仍然不是 F# 级别:

  • “在专用线程中异步运行”请求不是在调用中定义的,而是在 GetApplicationSettings() 方法本身上定义的!这个方法还不如改名为GetApplicationSettingsAsynchronously(),不是吗;
  • 还有,这不能帮助回调部分;
  • 更不用说使用更少线程的 F#“异步”功能了……我什至可以暂时放弃这个“高级”功能……

【问题讨论】:

  • 简化 C# 中的异步编码...
  • athos:我想你会对C# & .NET 5.0感兴趣
  • @Alastair : 谢谢,让我消化一下。我想知道 C# 什么时候发布?
  • 有一个 CTP 可用于测试,我猜测完整版本是明年某个时候。

标签: c# asynchronous f# callback aop


【解决方案1】:

正如 Brian 已经提到的,Visual Studio Async CTP 就是您要找的东西。它将异步工作流之类的东西添加到 C# 的未来版本中。它添加了类似于 F# 中的 异步工作流async 方法和行为类似于 let!await 关键字。两者有一些区别,我写了series of articles that compares them

不幸的是,C# 中的异步支持仍然是 CTP。如果您想编写生产质量的异步代码,最好的选择是用 F# 编写它并使用 Tasks 从 C# 引用它。这个SO question discusses how to do that

您可以通过各种方式在 C# 中模拟 async 之类的东西。 Brian 提到了使用 LINQ(在他的博客文章中),但这非常有限。我认为使用迭代器更灵活。这是来自article that does exactly that 的示例:

static IEnumerable<IAsync> DownloadAsync(string url)
{
  WebRequest req = HttpWebRequest.Create(url);
  Async<WebResponse> response = req.GetResponseAsync();
  yield return response;

  Stream resp = response.Result.GetResponseStream();
  Async<string> html = resp.ReadToEndAsync().ExecuteAsync<string>();
  yield return html;

  Console.WriteLine(html.Result);
}

这与async workflows 的工作方式类似,因为C# 迭代器也被编译为状态机。 Jeffrey Richter 的AsyncEnumerator implements a more complete API 就是基于这个想法。

【讨论】:

    【解决方案2】:

    http://msdn.microsoft.com/en-us/vstudio/async.aspx

    http://lorgonblog.wordpress.com/2008/05/07/an-introduction-to-async-workflows-in-f-or-how-to-utilize-all-those-cpus-without-writing-lots-of-threading-code-part-three/

    有用的信息。第一个链接是 CTP 到 C# 和 VB 的“异步”(将在这些语言的未来版本中出现),第二个链接显示了一种利用 LINQ 今天在 C# 中执行异步的方法。

    【讨论】:

      【解决方案3】:

      下面是 C# 中使用 FSharpx 的相同函数,看起来与 F# 版本几乎相同:

      FSharpAsync<IEnumerable<Tuple<DateTime, decimal>>> LoadPrices(string ticker) {
          var url = "http://ichart.finance.yahoo.com/table.csv?s=" + ticker + "&d=9&e=30&f=2008&g=d&a=2&b=13&c=1986&ignore=.csv";
          var req = WebRequest.Create(url);
          return
              from resp in req.FSharpAsyncGetResponse()
              let stream = resp.GetResponseStream()
              let reader = new StreamReader(stream)
              from csv in reader.FSharpAsyncReadToEnd()
              select (from line in csv.Split('\n').Skip(1)
                      let values = line.Split(',')
                      where values.Length == 7
                      select Tuple.Create(DateTime.Parse(values[0]), decimal.Parse(values[6], CultureInfo.InvariantCulture)));
      }
      

      示例用法:

      var price = LoadPrices("MSFT").Run().First();
      Assert.AreEqual(new DateTime(2008,10,30), price.Item1);
      Assert.AreEqual(20.82m, price.Item2);
      

      这里的关键概念是 LINQ 不仅仅是语言集成的查询:它是语言集成的monads。 FSharpx 仅实现了从 C#/VB.NET 中的 LINQ 和 it's pretty trivial code 访问 F# async 所需的绑定。

      FSharpx 需要 .NET 3.5 或更高版本,但原则上您可以在 C# 3、.NET 2.0 上进行这项工作。

      其中一个缺点是 LINQ 中没有 use!(也应该在原始 F# 代码中使用),因此管理一次性用品是有问题的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-02-01
        • 2013-09-09
        • 1970-01-01
        • 2011-06-02
        • 1970-01-01
        • 2021-09-20
        • 1970-01-01
        相关资源
        最近更新 更多