2

我正在尝试运行一个函数来更新用 f# 编写的生命游戏函数的网格,并且所有内容都必须是递归的,没有可变的。我想通过异步运行更新函数来向我的表单添加一个暂停按钮,但是当我这样做时,只有一个正方形被更新。但是,当我在没有异步的情况下单步执行程序时,所有方块都会更新。任何想法为什么?

let buttonGrid : Button list list = (Startup ar);;

//transform buttongrid to int grid
let rec bg2ig (bg:Button list list) = 
    let rec innerLoop (bl:Button list) =
        match bl with
        |[] -> []
        |x::xs -> if (x.Name = "0") then 0::(innerLoop xs) else 1::(innerLoop xs)
    match bg with
    |[] -> []
    |y::ys -> (innerLoop y)::(bg2ig ys)

let Update (bg:Button list list)=
    let ar = (bg2ig bg)
    let rec innerUpdate (bg:Button list list)= 
        let rec arrayLoop (bl:Button list) y = 
            match bl with
            |[] -> 0
            |x::xs -> 
                let X = (15-xs.Length)
                let n = (neighbors X y ar)
                if  (ar.[X].[y] = 0) then (if n=3 then buttonGrid.[X].[y].Name<-"1") else (if (n=2||n=3)=false then buttonGrid.[X].[y].Name<-"0")
                if buttonGrid.[15-xs.Length].[y].Name="0" 
                then buttonGrid.[15-xs.Length].[y].BackColor <- Color.White 
                else buttonGrid.[15-xs.Length].[y].BackColor <- Color.Black
                arrayLoop xs y
        match bg with
        |[] -> []
        |y::ys -> 
            ignore (arrayLoop y (15-ys.Length)) 
            innerUpdate ys
    innerUpdate bg

let Running = async {
    let rec SubRun (x:int) =
        ignore (Update buttonGrid)
        if x = 1 then
            SubRun 1
        else
            0
    ignore (SubRun 1)
    do! Async.Sleep(1000)
    }

let RunAll() = 
    Running
    |> Async.RunSynchronously
    |> ignore
4

2 回答 2

4

如评论中所述,Async.RunSynchronously对于这种情况,这是一个错误的功能。它在后台线程上启动工作流(这是错误的,因为您想访问 GUI 元素),然后它阻塞调用线程,直到后台工作完成(这是错误的,因为您不想阻塞 GUI 线程)。

您需要使用Async.StartImmediatewhich 在当前线程(将是 GUI 线程)上启动工作而不会阻塞。当工作流的第一部分完成时(之前Sleep),GUI 线程可以自由地做其他工作。之后Sleep工作流程将再次在 GUI 线程上继续(这是自动完成的StartImmediate),因此您可以再次访问 GUI。

另外,SubRun执行实际循环的函数也需要是异步的 - 所以我希望循环的主要部分看起来像这样:

let Running = async {
    let rec SubRun (x:int) = 
        // Perform update and then sleep before recursive call
        ignore (Update buttonGrid)
        do! Async.Sleep(1000)
        if x = 1 then
            return! SubRun 1
        else
            return 0 }

    // Start the loop and asynchronously ignore the result
    SubRun 1 |> Async.Ignore

let RunAll() = 
    // Start the computation immediately on the current threada
    Running |> Async.StartImmediate
于 2012-12-31T00:51:47.767 回答
0

Tomas Petricek 解决了我最初遇到的问题,但为了使事情正确,我最终以不同的方式解决了它。我认为我最初的问题可能源于错误地更新表格或根本没有更新表格,因此看起来非常错误。

我最终像这样编写了我的异步函数

let rec async1(syncContext, form : System.Windows.Forms.Form, cancellationSource:CancellationTokenSource, (limit:int)) =
    async {
        do! Async.SwitchToContext(syncContext)
        ignore (Update buttonGrid)

        do! Async.SwitchToThreadPool()

        do! Async.Sleep(300)
        if limit > 1 then
            ignore (Async.Start (async1(syncContext, form, cancellationSource, (limit-1)),cancellationSource.Token))
        else if limit = -1 then
            ignore (Async.Start (async1(syncContext, form, cancellationSource, limit),cancellationSource.Token))
    }

然后我可以像这样用开始和停止按钮调用它

let b = new Button(Location=new Point(50,500), Text=("Run"), Width=100, Height=40)
let btnPause = new Button(Location=new Point(150, 500), Text="Stop", Width=100, Height=40, Enabled=false)
b.Click.Add(fun _ -> 
    let cancellationSource = new CancellationTokenSource()
    b.Enabled <- false
    btnPause.Enabled <- true
    btnSave.Enabled <- false
    btnLoad.Enabled <- false
    btnStep.Enabled <- false
    inputBox.Enabled <- false
    btnPause.Click.Add(fun _ -> 
        b.Enabled <- true
        btnPause.Enabled <- false
        btnSave.Enabled <- true
        btnLoad.Enabled <- true
        btnStep.Enabled <- true
        inputBox.Enabled <- true
        cancellationSource.Cancel())
    ignore (Async.Start (async1(syncContext, form, cancellationSource, (int inputBox.Text)),cancellationSource.Token))
    ignore (inputBox.Text <- "0"))

我还添加了一个用于单步执行程序的步骤按钮和一个输入框,我可以在其中无休止地运行程序,直到调用取消令牌或让它运行 n 次然后停止

于 2012-12-31T18:01:19.137 回答