C #: как реализовать как «асинхронно», так и «!» в F #? - PullRequest
2 голосов
/ 01 августа 2011

Читая недавно F #, мне кажется, что "async" и "!" (Bang!: P) довольно полезные функции. (Не по теме, это пример того, что язык программирования является не только темой академических исследований, но и реальной, продуктивной темой проектирования).

Ниже из презентации 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" и "!" функции? Например, возможно ли выполнить «асинхронный ... 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').....;
    }
}

пс. Я знаю, некоторые из вас, ребята, скажут: Афон, просто используйте F #! да ... я не против F #, но нужно время, чтобы изменить весь отдел, прежде чем я надеюсь позаимствовать некоторые достоинства у F #.

Я знаю, что есть некоторая концепция программирования Aspect для расширения возможностей C #. Однако он все еще не достиг уровня F #.

Например, я могу расширить PostSharp с http://www.sharpcrafters.com, чтобы путем простого присвоения атрибута методу принудительно запускать его в выделенном потоке:

[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 # для использования меньшего количества потоков ... сейчас я могу даже отказаться от этой "расширенной" функции ...

Ответы [ 3 ]

4 голосов
/ 01 августа 2011

Как уже упоминал Брайан, Visual Studio Async CTP - это то, что вы ищете.Он добавляет что-то вроде асинхронных рабочих процессов в будущую версию C #.Он добавляет async методы, которые похожи на асинхронные рабочие процессы в F # и await, которые ведут себя как let!.Между ними есть несколько различий, и я написал серию статей , в которой их сравнивают .

К сожалению, асинхронная поддержка в C # все еще является CTP.Если вы хотите написать асинхронный код производственного качества, лучше всего написать его на F # и ссылаться на него из C #, используя Tasks.В этом вопросе SO обсуждается, как это сделать .

Вы можете моделировать что-то вроде async в C # различными способами.Брайан упомянул об использовании 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 # также компилируются как конечный автомат.AsyncEnumerator Джеффри Рихтера реализует более полный API , основанный на этой идее.

2 голосов
/ 16 марта 2012

Вот та же функция в 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 - это больше, чем просто языковая интеграция запросов : это языковая интеграция монады .FSharpx просто реализует привязки, необходимые для доступа к F # async из LINQ в C # / VB.NET, а это довольно тривиальный код .

FSharpx требует .NET 3.5 или лучше, но в принципе вы могли бысделайте это на C # 3, .NET 2.0.

Одним из недостатков является то, что в LINQ нет use! (который также должен был использоваться в исходном коде F #), поэтому управление расходными материалами проблематично.

2 голосов
/ 01 августа 2011

См

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 #.

...