• F# 天生就是就异步和并行的料


    做模型开发免不了要使用异步和并行计算,尤其在多核CPU的今天,更是如此,F#恰逢其时,天生就具备这种能力,先看一个例子。

    open System

    open System.Drawing

    open System.Windows.Forms

    open System.Threading

     

    let r = Random()

    let drawCurves (frm : Form) =

        let x, y = frm.ClientSize.Width, frm.ClientSize.Height

        let g = frm.CreateGraphics()

        for i in 1..10 do

            Thread.Sleep(300)

            g.DrawCurve(Pens.Black, [|  for i in 1..5 -> Point(r.Next(x), r.Next(y)) |])

        g.Dispose()

           

    let drawRecs (frm : Form) =

        let x, y = frm.ClientSize.Width, frm.ClientSize.Height

        let g = frm.CreateGraphics()

        for i in 1..10 do

            Thread.Sleep(150)

            g.DrawRectangle(Pens.Blue, r.Next(x), r.Next(y), r.Next(x/2), r.Next(y/2))

        g.Dispose()

     

    let drawElls (frm : Form) =

        let x, y = frm.ClientSize.Width, frm.ClientSize.Height

        let g = frm.CreateGraphics()

        for i in 1..10 do

            Thread.Sleep(150)

            g.DrawEllipse(Pens.OrangeRed, r.Next(x), r.Next(y), r.Next(x/2), r.Next(y/2))

        g.Dispose()

     

    let frm1 = new Form(Text="随机图形", BackColor=Color.White)

    frm1.DoubleClick.Add(fun e -> Async.RunSynchronously

                                                    (Async.Parallel ([ async { drawCurves frm1 };

                                                    async { drawRecs frm1 };

                                                    async { drawElls frm1 }

                                                    ]))    |> ignore

                                                    )

    Application.Run(frm1)

     

    这是用F#编写的WinForm程序,是在Form上随机画矩形和椭圆和曲线,所不同是使用异步和并行方法实现的。

    异步工作流

    当使用.NET BCL的所有I/O操作的时候,有两个模型可用,同步模型和异步模型。异步模型是通过一个通用的编程模式来支持的,即成对出现的BeginXXX和 EndXXX方法。程序员通过调用BeginXXX来开始异步操作,这个方法开始执行后,就马上返回。而,程序员在得到异步操作已经结束的提醒后,必须调用EndXXX方法完成整个过程。这就是所谓的分两步实现异步操作。

    而F#通过异步工作流来实现异步的,并不需要两步,只需要一步就行,这大大简化异步操作。

    关键的 let! 异步绑定

    在异步工作流中,一些表达式和操作是同步的,而另一些则是旨在以异步方式返回结果的较长时间的计算。 以异步方式调用一个方法时,使用let!,而不是普通的 let 绑定。 let! 的作用是允许在执行计算的同时,继续执行其他计算或线程。 在 let! 绑定的右侧返回后,异步工作流的剩余部分将继续执行。

    在下面的代码示例中,函数 fetchAsync 将获取从 Web 请求返回的 HTML 文本。 fetchAsync 函数包含异步代码块。 在对异步基元的结果(此示例中为 AsyncDownloadString)执行绑定时,将使用 let!,而不是使用 let。

    open System.Net

    open Microsoft.FSharp.Control.WebExtensions

    let urlList = [ "Microsoft.com", "http://www.microsoft.com/"

                    "MSDN", "http://msdn.microsoft.com/"

                    "Bing", "http://www.bing.com"

                  ]

     

    let fetchAsync(name, url:string) =

        async {

            try

                let uri = new System.Uri(url)

                let webClient = new WebClient()

                let! html = webClient.AsyncDownloadString(uri)

                printfn "Read %d characters for %s" html.Length name

            with

                | ex -> printfn "%s" (ex.Message);

        }

     

    let runAll() =

        urlList

        |> Seq.map fetchAsync

        |> Async.Parallel

        |> Async.RunSynchronously

        |> ignore

     

    runAll()

    从这个例子可以看出,编写异步程序,跟编写同步程序差别不大,只是多了个let!和async块而已,多简单呢!

    下面我们看看并行

    F#使用Async.Parallel 方法构建一个工作流,将并行执行列表中的所有工作流。在执行时,由Async.Parallel组合而成的异步操作会通过一个等待计算的队列来逐步发起。Async.Parallel只能处理固定数量的任务,对于一边处理一边生成任务的情况不能胜任。换个方式来看,即Async.Parallel无法处理即时获得的消息──例如,除了取消任务之外,一个代理对象的工作进度是可以得到控制的。另外,F#还可以利用.NET的并行计算机制实现并行。

    下面的例子是在一个随机3千万数组中求最大值代码,我们看看并行与串行比较结果。

    open System

    open System.Linq

    open System.Diagnostics

     

    let rand = System.Random()

    let arr1 = [| for i in 0..1000000 -> float(rand.Next(1000000)) * rand.NextDouble() |]

     

    let watch1 = Stopwatch()

     

    watch1.Start()

    let x = Seq.max arr1

    watch1.Stop()

     

    printfn "数组长度=%i, x=%.4f,  串行计算用时%i毫秒" arr1.Length x watch1.ElapsedMilliseconds

     

    let watch2 = Stopwatch()

    watch2.Restart()

    let pArr1 = ParallelEnumerable.AsParallel arr1;

    let x' = ParallelEnumerable.Max pArr1

    watch2.Stop()

     

    printfn "数组长度=%i, x=%.4f,  串行计算用时%i毫秒" arr1.Length x watch2.ElapsedMilliseconds

    Console.ReadKey() |> ignore

    在一台i3-2120CPU 3.3G惠普机器上运行的结果:

     

    并行有性能明显优势。

    那么是不是并行就一定比串行快呢?,我们把3千万的数组改为1百万的数组在看看结果:

     

    很明显,并行反倒比串行慢!!!,这一点请大家一定要注意,不要想当然认为并行一定比串行快。

  • 相关阅读:
    夺命雷公狗---ECSHOP---08---商品页的拇改成星星
    夺命雷公狗---ECSHOP---07---商品价格的遍历
    夺命雷公狗---ECSHOP---06---商品倒计时的实现
    WordPress博客密码忘记的解决方法
    夺命雷公狗---js_mv思路
    LAMP前一定要关闭防火墙
    夺命雷公狗---Smarty NO:25 缓存控制技术2(完结)
    夺命雷公狗---Smarty NO:24 缓存控制技术1
    夺命雷公狗---Smarty NO:23 常用方法
    夺命雷公狗---Smarty NO:22 常量—变量
  • 原文地址:https://www.cnblogs.com/junxian_chen/p/4195962.html
Copyright © 2020-2023  润新知