为了帮助回答我的问题,我已经准备了以下尽可能简单的WinForms代码。你可以看到我有一个开始按钮,它设置并并行运行3个不同的异步计算,每个异步计算都做一些工作,然后用结果更新标签。我有3个取消按钮对应于每个异步计算是并行运行的。如何连接这些取消按钮以取消其相应的异步计算,同时允许其他按钮继续并行运行?谢谢!
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 _ -> ()))发布于 2011-05-02 08:41:08
以不合作的方式取消线程通常是一种糟糕的做法,因此我不建议这样做。有关示例this article,请参阅。当您直接使用Thread (使用Thread.Abort)进行编程时,可以做到这一点,但是现代的.NET并行/异步库(如第三方公共语言或F#异步)都没有使用这种方法。如果这确实是您需要的,那么您必须显式地使用线程。
更好的选择是更改work函数,以便可以协同取消它。在F#中,这实际上意味着将其包装在async中并使用let!或do!,因为这会自动插入对取消的支持。例如:
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意味着要被取消,那么它应该意识到这种情况,以便它可以适当地处理它。
发布于 2011-05-03 07:38:32
正如Tomas提到的,强行停止线程是一个坏主意,在我看来,设计一些没有意识到它是一个能够停止的线程的东西会引起一些标志,但是,如果你正在做一个很长的计算,如果你使用一些数据结构,比如2或3D数组,那么一种选择是能够将其设置为null,但是,这违反了F#的许多概念,因为你的函数所处理的不仅应该是不可变的,而且如果有一些数组要被改变,那么其他任何东西都不应该改变它。
例如,如果你需要停止一个正在处理文件的线程(我之前必须这样做),那么,由于该文件不能被删除,因为它是打开的,所以我可以在记事本中打开它,然后删除所有内容,然后保存它,然后线程崩溃。
因此,您可能想要这样做,以实现您的目标,但是,我建议您重新评估您的设计,看看是否有更好的方法来做到这一点。
https://stackoverflow.com/questions/5852317
复制相似问题