2014-12-22 54 views
3

我有一个循环,我想停止使用按钮。 编辑为更好的理解:如何使用按钮停止for循环?

我知道你不能停止一个按钮,而一个循环运行,因为它不会工作,只要当前的用户界面正在运行。我真正要求的是创建线程或使用BGWorker来阻止这种情况的最有效方式。我见过一些方法,但其中大部分是针对Java而不是C#的。

我想要做的是:

private void start_Click(object sender, EventArgs e) 
{ 

    for(int i = 0; i < nums; i++) 
    { 
     doSomething(); 
    } 
} 

private void stop_Click(object sender, EventArgs e) 
{ 

    stops start_Click() 
} 
+4

除非将循环放置在另一个线程中,否则将无法执行所要求的操作。您的循环阻止与您按钮的交互。 –

+1

我知道人们不会喜欢这个,但通过把'Application.DoEvents()'和检查循环中的布尔变量来试试它:) –

+0

您是使用WinForms还是WPF或其他? –

回答

7

你不能做到这一点。对于初学者来说,for循环在UI线程上同步运行,这意味着您甚至不会通过单击“停止”按钮。

因此,您需要将for循环的操作移至另一个线程,这意味着您可能根本不会使用for循环。您需要考虑实际上需要执行的代码,然后根据您如何进行处理,您可以实现“停止”按钮。

一个非常简单的方式做,这是只:

new Thread(() => 
{ 
    int i = 0; 
    while (!stop && i < num) 
    { 
     doSomething(); 
     i++; 
    } 
}).Start(); 

并设置stop停止加工循环。在更现实的情况下,您可以排队想要处理的函数,然后通过类似的方法停止出列。不幸的是,它很难在不知道更多细节的情况下推荐一个设置。

任何基于您的代码的解决方案也会有当前doSomething()完成执行(可能需要一段时间)的问题。再次,没有更多的信息,很难说什么是解决问题的最佳方法。

-1

试试这个:

bool stop=false; 

private void start_Click(object sender, EventArgs e) 
{ 
for(int i = 0; i < nums&& !bool; i++) 
{ 
doSomething(); 
} 
} 

,并在click事件

设置

stop=true; 
+2

将无法​​工作,UI线程被阻止。如果在*单独的*线程上执行,这会起作用,但可能会导致其他问题... – BradleyDotNET

+2

这不起作用,因为UI已经在运行当前任务,并且在完成之前不会运行单独的任务。 – Xceptionist

5

为了使您的UI响应可以取消你可以使用一个backgrounworker运行操作。 BackgroundWorker的确实在其他线程的工作,同时保持你的UI响应:

private readonly BackgroundWorker _backgroundWorker; 

    public Form1() 
    { 
     InitializeComponent(); 

     _backgroundWorker = new BackgroundWorker 
     { 
      WorkerSupportsCancellation = true 
     }; 
     _backgroundWorker.DoWork += backgroundWorker_DoWork; 
     _backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted; 

     Disposed += Form1_Disposed; 
    } 

    private void Form1_Disposed(object sender, EventArgs e) 
    { 
     _backgroundWorker.Dispose(); 
    } 

    private void StartLoop() 
    { 
     if (!_backgroundWorker.IsBusy) 
     { 
      _backgroundWorker.RunWorkerAsync(); 
     } 
    } 

    private void StopLoop() 
    { 
     _backgroundWorker.CancelAsync(); 
    } 

    private void backgroundWorker_DoWork(object sender , DoWorkEventArgs e) 
    { 
     var backgroundWorker = (BackgroundWorker) sender; 

     for (var i = 0; i < 100; i++) 
     { 
      if (backgroundWorker.CancellationPending) 
      { 
       e.Cancel = true; 
       return; 
      } 
      // Do Work 
     } 
    } 

    private void backgroundWorker_RunWorkerCompleted(object sender , RunWorkerCompletedEventArgs e) 
    { 
     if (e.Cancelled) 
     { 
      // handle cancellation 
     } 
     if (e.Error != null) 
     { 
      // handle error 
     } 

     // completed without cancellation or exception 
    } 
+0

只是一个抬头......即时将要窃取此代码。 – DidIReallyWriteThat

+0

这太棒了!虽然我宁愿有办法在多线程中做到这一点,但这有很大的帮助。谢谢! – Xceptionist

+0

在MSDN上检查它清楚地显示了一些可能性,你可以使用:http://msdn.microsoft.com/en-us/library/z8chs7ft%28v=vs.110%29.aspx – Sievajet

2

恕我直言,这可能是最好的方法在这里是你的工作转化为一个异步操作,然后使用async/await成语的循环。例如:

private bool _stopLoop; 

private async void start_Click(object sender, EventArgs e) 
{ 
    _stopLoop = false; 

    for(int i = 0; i < nums && !_stopLoop; i++) 
    { 
     await Task.Run(() => doSomething()); 
    } 
} 

private void stop_Click(object sender, EventArgs e) 
{ 
    _stopLoop = true; 
} 

这使得环本身在该_stopLoop变量是被管理的UI线程中执行,但实际上不阻塞UI线程(除其他事情会阻止“停止”被按下按钮)。

不幸的是,您没有提供有关doSomething()工作原理的详细信息。可能有一种很好的方法可以将整个方法转换为async方法,但如果没有实际的代码,我不能对此进行评论。

请注意,这种方法只会在每个操作之间的某个点处中断循环。如果您希望能够中断doSomthing()操作本身,则必须为此提供一种机制。一种可能的方法是使用CancellationSourceCancellationToken,这提供了表达取消语义的便利方式。

0

尝试使用异步/等待方法。这很容易!

public partial class MyForm : Form 
{ 
    public MyForm() 
    { 
     InitializeComponent(); 
    } 

    private CancellationTokenSource _tokenSource; 

    private async void start_Click(object sender, EventArgs e) 
    { 
     if (_tokenSource != null) 
      return; 

     _tokenSource = new CancellationTokenSource(); 
     var ct = _tokenSource.Token; 

     await Task.Factory.StartNew(() => 
     { 
      for (; ;) 
      { 
       if (ct.IsCancellationRequested) 
        break; 
       doSomething(); 
      } 
     }, ct); 

     _tokenSource = null; 
    } 

    private int _labelCounter; 

    private void doSomething() 
    { 
     // do something 
     Invoke((Action)(() => 
     { 
      myLabel.Text = (++_labelCounter).ToString(); 
     })); 
    } 

    private void stop_Click(object sender, EventArgs e) 
    { 
     if (_tokenSource == null) 
      return; 

     _tokenSource.Cancel(); 
    } 
} 
+0

这个问题是,目前使用Visual Studio 2010,并且不能使用async/await。是否下载了更新的版本,或者我将无法使用2013版运行以前的程序? – Xceptionist

+0

这不是问题,MS试图在这种情况下保持完全向后兼容。您的项目将转换为VS 2013项目,您应该在项目属性中选择相应的targer框架(例如4.5.1)。就这样。 Async/await是异步UI编程的极大简化,值得。 –

+0

感谢您的信息! (: – Xceptionist