繁体   English   中英

C#:如何在F#中实现“async”和“!”?

[英]C#: how to implement as “async” and “!” in F#?

最近阅读F#,结果是“async”和“!” (Bang!: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”和“!” 特征? 例如,是否可以按照以下方式或以类似的轻松方式执行“异步...运行...回调”?

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#“异步”功能使用更少的线程...我甚至可以放弃这个“advaced”功能...

正如Brian已经提到的, Visual Studio Async CTP正是您所需要的。 它为未来版本的C#添加了类似异步工作流程的功能。 它添加了async方法,就像F#中的异步工作流一样 ,并且await关键字的行为类似于let! 两者之间存在一些差异,我写了一系列比较它们的文章

不幸的是,C#中的异步支持仍然是CTP。 如果要编写生产质量的异步代码,最好的选择是在F#中编写它并使用Tasks从C#引用它。 这个SO问题讨论了如何做到这一点

您可以通过各种方式模拟C#中的async Brian提到使用LINQ(在他的博客文章中),但这是非常有限的。 我认为使用迭代器更灵活。 以下是文章中的一个示例:

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基于这个想法实现了更完整的API

看到

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-的线程代码部分三/

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

这是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不仅仅是语言集成查询 :它是语言集成的monad FSharpx只是在C#/ VB.NET中实现从LINQ访问F#async所需的绑定,而且它是非常简单的代码

FSharpx需要.NET 3.5或更高版本,但原则上你可以在C#3,.NET 2.0上使用它。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM