我有一个循环,我想停止使用按钮。 编辑以便更好地理解:
我确实意识到在循环运行时你无法停止按钮,因为只要当前的UI正在运行它就无法工作。我真正要求的是创建线程或使用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()
}
答案 0 :(得分:7)
你做不到。对于初学者来说,for
循环在UI线程上同步运行,这意味着你甚至不能能够点击“停止”按钮。
因此,您需要将for循环的操作移动到另一个线程,这意味着您可能根本不会使用for
循环。您需要考虑实际中的代码是如何执行的,然后根据您执行处理的方式,您可以实现“停止”按钮。
一个非常简单的方法就是:
new Thread(() =>
{
int i = 0;
while (!stop && i < num)
{
doSomething();
i++;
}
}).Start();
并设置stop
以停止处理循环。在更现实的情况下,您可以排队要处理的函数,然后通过类似的方法停止出列。不幸的是,很难在不了解更多细节的情况下推荐设置。
任何基于您的代码的解决方案也会遇到当前doSomething()
完成执行的问题(可能需要一段时间)。再说一遍,没有更多的信息,很难说最好的解决方法是什么。
答案 1 :(得分:5)
为了让您的UI响应能够取消正在运行的操作,您可以使用背景工作者。 后台工作人员在保持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
}
答案 2 :(得分:2)
恕我直言,这里最好的方法是将您的工作转换为异步操作,然后使用async
/ await
成语进行循环。 E.g:
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线程(除此之外还会阻止&#34;停止&#34;按钮被点击)。
很遗憾,您没有提供有关doSomething()
工作原理的详细信息。可能有一种很好的方法可以将整个方法转换为async
方法,但如果没有实际代码,我无法对此进行评论。
请注意,此方法仅在每个操作之间的某个点中断循环。如果您希望能够中断doSomthing()
操作本身,那么您必须为此提供一种机制。一种可能的方法是使用CancellationSource
和CancellationToken
,它提供了表达取消语义的便捷方式。
答案 3 :(得分:0)
尝试使用async / await方法。这很容易!
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();
}
}
答案 4 :(得分:-1)
试试这个:
bool stop=false;
private void start_Click(object sender, EventArgs e)
{
for(int i = 0; i < nums&& !bool; i++)
{
doSomething();
}
}
并在点击事件中
设置
stop=true;