首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从按钮单击事件取消与其他异步计算并行运行的单个异步计算

如何从按钮单击事件取消与其他异步计算并行运行的单个异步计算
EN

Stack Overflow用户
提问于 2011-05-02 07:57:42
回答 2查看 295关注 0票数 1

为了帮助回答我的问题,我已经准备了以下尽可能简单的WinForms代码。你可以看到我有一个开始按钮,它设置并并行运行3个不同的异步计算,每个异步计算都做一些工作,然后用结果更新标签。我有3个取消按钮对应于每个异步计算是并行运行的。如何连接这些取消按钮以取消其相应的异步计算,同时允许其他按钮继续并行运行?谢谢!

代码语言:javascript
复制
open System.Windows.Forms

type MyForm() as this =
    inherit Form()
    let lbl1 = new Label(AutoSize=true, Text="Press Start")
    let lbl2 = new Label(AutoSize=true, Text="Press Start")
    let lbl3 = new Label(AutoSize=true, Text="Press Start")

    let cancelBtn1 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
    let cancelBtn2 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
    let cancelBtn3 = new Button(AutoSize=true,Enabled=false, Text="Cancel")

    let startBtn = new Button(AutoSize=true,Text="Start")

    let panel = new FlowLayoutPanel(AutoSize=true, Dock=DockStyle.Fill, FlowDirection=FlowDirection.TopDown)
    do
        panel.Controls.AddRange [|startBtn; lbl1; cancelBtn1; lbl2; cancelBtn2; lbl3; cancelBtn3; |]
        this.Controls.Add(panel)

        startBtn.Click.Add <| fun _ ->
            startBtn.Enabled <- false
            [lbl1;lbl2;lbl3] |> List.iter (fun lbl -> lbl.Text <- "Loading...")
            [cancelBtn1;cancelBtn2;cancelBtn3] |> List.iter (fun cancelBtn -> cancelBtn.Enabled <- true)

            let guiContext = System.Threading.SynchronizationContext.Current

            let work (timeout:int) = //work is not aware it is being run within an async computation
                System.Threading.Thread.Sleep(timeout)
                System.DateTime.Now.Ticks |> string

            let asyncUpdate (lbl:Label) (cancelBtn:Button) timeout =
                async {
                    let result = work timeout //"cancelling" means forcibly aborting, since work may be stuck in an infinite loop
                    do! Async.SwitchToContext guiContext
                    cancelBtn.Enabled <- false
                    lbl.Text <- result
                }

            let parallelAsyncUpdates =
                [|asyncUpdate lbl1 cancelBtn1 3000; asyncUpdate lbl2 cancelBtn2 6000; asyncUpdate lbl3 cancelBtn3 9000;|]
                |> Async.Parallel
                |> Async.Ignore

            Async.StartWithContinuations(
                parallelAsyncUpdates,
                (fun _ -> startBtn.Enabled <- true),
                (fun _ -> ()),
                (fun _ -> ()))
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-05-02 08:41:08

以不合作的方式取消线程通常是一种糟糕的做法,因此我不建议这样做。有关示例this article,请参阅。当您直接使用Thread (使用Thread.Abort)进行编程时,可以做到这一点,但是现代的.NET并行/异步库(如第三方公共语言或F#异步)都没有使用这种方法。如果这确实是您需要的,那么您必须显式地使用线程。

更好的选择是更改work函数,以便可以协同取消它。在F#中,这实际上意味着将其包装在async中并使用let!do!,因为这会自动插入对取消的支持。例如:

代码语言:javascript
复制
let work (timeout:int) = async {
  do! Async.Sleep(timeout)
  return System.DateTime.Now.Ticks |> string }

在不使用async的情况下(例如,如果函数是用C#编写的),您可以传递一个CancellationToken值并使用它来检查是否请求取消(通过调用ThrowIfCancellationRequestsd)。然后,您可以使用Async.Start开始这三个计算(为每个计算创建一个新的CancellationTokenSource )。

要做一些事情,直到它们都完成,我可能会创建一个简单的代理(它触发一些事件,直到它接收到指定数量的消息)。我认为没有更直接的方法可以做到这一点(因为Async.Parallel对所有工作流都使用相同的取消令牌)。

所以,我猜这个答案的要点是-如果work意味着要被取消,那么它应该意识到这种情况,以便它可以适当地处理它。

票数 3
EN

Stack Overflow用户

发布于 2011-05-03 07:38:32

正如Tomas提到的,强行停止线程是一个坏主意,在我看来,设计一些没有意识到它是一个能够停止的线程的东西会引起一些标志,但是,如果你正在做一个很长的计算,如果你使用一些数据结构,比如2或3D数组,那么一种选择是能够将其设置为null,但是,这违反了F#的许多概念,因为你的函数所处理的不仅应该是不可变的,而且如果有一些数组要被改变,那么其他任何东西都不应该改变它。

例如,如果你需要停止一个正在处理文件的线程(我之前必须这样做),那么,由于该文件不能被删除,因为它是打开的,所以我可以在记事本中打开它,然后删除所有内容,然后保存它,然后线程崩溃。

因此,您可能想要这样做,以实现您的目标,但是,我建议您重新评估您的设计,看看是否有更好的方法来做到这一点。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5852317

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档