C# 如何使用按钮启动和停止连续运行的后台工作程序
假设我有这样一个背景工人:C# 如何使用按钮启动和停止连续运行的后台工作程序,c#,winforms,backgroundworker,C#,Winforms,Backgroundworker,假设我有这样一个背景工人: private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { while(true) { //Kill zombies } } private void startButton_Click(System.Object sender,
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//Kill zombies
}
}
private void startButton_Click(System.Object sender,
System.EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
}
private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e)
{
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
}
我怎样才能让这个后台工作人员开始和停止使用WinForm上的按钮?我还没有测试过这个,我有一些代码,我必须要看清楚我到底做了什么,但这是Fredrik答案的改编:
private bool _performKilling;
private object _lockObject = new object();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
if (_performKilling)
{
//Kill zombies
}
else
{ //We pause until we are woken up so as not to consume cycles
Monitor.Wait(_lockObject);
}
}
}
private void StartKilling()
{
_performKilling = true;
Monitor.Pulse(_lockObject);
}
private void StopAllThatKilling()
{
_performKilling = false;
]
此模式的更完整示例如下:
也许你可以使用像这样的手动重置事件,我没有调试它,但值得一试。如果它起作用,你就不会让线在等待的时候转动轮子
ManualResetEvent run = new ManualResetEvent(true);
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(run.WaitOne())
{
//Kill zombies
}
}
private void War()
{
run.Set();
}
private void Peace()
{
run.Reset();
}
使用该方法
在工作线程内的循环中
if (backgroundWorker.CancellationPending) return;
这种情况不会立即发生。停止是指停止还是暂停? 如果你的意思是停止,那么这是小菜一碟。为要负责启动后台工作程序的按钮创建一个按钮单击事件处理程序,并为负责停止后台工作程序的按钮创建一个按钮单击事件处理程序。在开始按钮上,调用触发do_work事件的后台工作程序方法。大概是这样的:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//Kill zombies
}
}
private void startButton_Click(System.Object sender,
System.EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
}
private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e)
{
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
}
在停止按钮上,调用将后台工作人员的CancellationPending
设置为true的方法,如下所示:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//Kill zombies
}
}
private void startButton_Click(System.Object sender,
System.EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
}
private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e)
{
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
}
现在别忘了检查后台工作人员的doWork中是否有CancelationPending
标志。大概是这样的:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while(true)
{
//Kill zombies
}
}
private void startButton_Click(System.Object sender,
System.EventArgs e)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
private void cancelAsyncButton_Click(System.Object sender,
System.EventArgs e)
{
// Cancel the asynchronous operation.
this.backgroundWorker1.CancelAsync();
}
private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e)
{
while (true)
{
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
}
还有你的嫁妆方法:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
KillZombies(worker, e);
}
我希望这能把你引向正确的方向。进一步阅读:
啊,当然了;很好的解决方案。我一定很累了,从一开始我就没想到;)“我没有对此进行测试”--很明显,因为对
Wait()
和Pulse()
的调用将引发异常,因为您在调用这些方法之前没有使用监视器。@PeterDuniho当然这里没有显示一些管道。只是想让我们在监视器上使用两种方法来促进暂停/取消暂停。这里有一个更完整的例子:“访问和更新bool是一个原子操作”。这是因为内存障碍吗?不,原子意味着值true或false将一次性写入。内存障碍指示另一个线程看到值的写入或读取顺序。没有任何东西表明,如果没有这种机制,写入_performKilling的值在另一个线程中是可见的。原子性意味着读取的值总是true或false,这并不意味着后台线程会看到最近写入的值。它们是原子的,但为了安全起见,我可能会将其标记为volatile。这个紧环也不需要呼吸。它一直在以最快的速度构建数据,例如,我不会发布任何来自此循环的无效调用。一定要将标志标记为volatile,否则您可能会看到Heisenbug。是的,我应该澄清,通过线程安全,我指的是值将“一次性”写入和读取的事实。标记标志volatile
可能会稍微提高访问顺序(但不是完全),因此为了在访问顺序和数据新鲜度方面真正安全起见,可能应该引入某种锁定机制(我将用它更新答案)(+1很高兴看到另一个真正理解.NET线程的灵魂。)我认为在这些情况下使用WaitHandle总是更好的选择,因为它们本质上为您解决了原子性和其他多线程问题(假设您选择了正确的WaitHandle类型)。while(true){if(run.WaitOne()){}
?看起来有点多余,不是吗?为什么不while(run.WaitOne()){}
?确实如此,我想我自己在发布后会更新它+1:我不明白为什么每个人都说要检查,而(_someCustomBoolean)
当BackgroundWorker
类已经内置了这个功能。一旦设置了取消挂起,我认为一般的协议是让后台工作人员停止并完成。在这种情况下,我相信这些问题是在寻找一种开始和结束的方式。背景工作人员也会让你绊倒。如果您使用它的oncompleted事件,这将通过.net通用线程池发布,我以前见过这个死锁,这取决于在那里抛出的其他内容。嗯,对于您接受的答案,请注意-您担心的是一个只会导致它不停止一两次迭代的效果,但是忘了你将要忙于整个处理器的旋转!至少在那里睡一觉。它看起来会起作用,但会造成很大的损害。其他一些答案解决了所有这些问题,就像你“应该”解决的那样。