C#按下按钮跳出循环

3

我有一个简单的C# foreach循环,当按下按钮时如何跳出循环?它不在backgroundWorker线程中,因此无法使用backgroundWorker.CancellationPending。


1
foreach循环是在单独的线程上使用还是在UI线程上使用? - Lloyd Powell
循环在另一个线程上运行吗? - AaronLS
1
如果循环在UI线程上,则在包含循环的函数完成之前,按钮单击事件处理程序将无法被触发。 - Lloyd Powell
如果你只是想定期检查它,那么我同意使用事件有点过头了。 - EFraim
为什么会有踩票?人们认为在按钮按下时中止循环是不明智的吗? - quillbreaker
3个回答

6
在表单中创建一个布尔标志。将事件处理程序附加到按钮的点击事件,并在事件处理程序中将标志设置为true。
在循环中检查标志,如果为true,则调用"break"。(更好的选择是使用while循环而不是for循环,在while条件中检查标志)
显然,for循环需要在某种形式的后台线程上运行,否则GUI将无响应。如果您不知道如何做到这一点,请查看ThreadPool.QueueUserWorkItemBackgroundWorker。如果您使用BackgroundWorker,可以使用内置的CancelAsync()方法而不是编写自己的取消标志。

[编辑:正如其他人(下面)指出并在这里这里中更深入地讨论的那样;您需要在访问布尔值之前锁定它或将其标记为易失性]

[不要忘记在开始循环之前设置标志的正确状态。]


4
显然,标志必须是易失性的,或者访问必须是同步的。 - EFraim
3
Efraim,为什么你需要同步访问一个简单的布尔字段?用户不可能同时按下启动循环的按钮和取消循环的按钮,是吗? - Dan Rosenstark
1
因为编译器可以合法地优化掉任何这样的访问,如果它认为有必要的话。 - EFraim
1
@Simon: 最糟糕的情况实际上是它会永远循环,因为编译器永远不会尝试访问该变量。 - EFraim
4
即使是一个布尔变量,当从另一个线程访问时,也需要使用lock(作为内存屏障)或volatile来保证其会被重新读取;否则可能会出现它被卡在寄存器中的示例:https://dev59.com/lHRB5IYBdhLWcg3w9Lvn#458193 - Marc Gravell
显示剩余5条评论

4

这里有一个不太好的解决方案。

在每次循环迭代中调用Application.DoEvents()。在按钮点击时将变量设置为True,在循环中检查该变量。如果为True,则跳出循环,否则继续执行。

正如我所说,这不是一个非常好的解决方案。Application.DoEvents()可能非常危险。最好的选择是使用后台线程。


2
我同意。这不是一个很好的解决方案。不要使用DoEvents。请使用BackgroundWorker或ThreadPool.QueueUserWorkItem()将for循环放在后台线程上。 - Simon P Stevens
即使它不可扩展且相当丑陋,但如果某个解决方案确实有效,那么就给它加上+1吧...您可以向其中添加计数器,并在每n次迭代时调用DoEvents以减少性能损失。 - Guffa

0

你可以像Aamir建议的那样使用DoEvents作为快速解决方案。

对于更可扩展和可维护的解决方案,您需要将工作移动到单独的线程中。然后,让按钮单击事件设置一个标志以停止循环就相当简单了。另一方面,需要更多的工作来将结果传递回UI线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接