Ian*_*oyd 119 .net c# multithreading backgroundworker
考虑一个对象的假设方法,为您做一些事情:
public class DoesStuff
{
BackgroundWorker _worker = new BackgroundWorker();
...
public void CancelDoingStuff()
{
_worker.CancelAsync();
//todo: Figure out a way to wait for BackgroundWorker to be cancelled.
}
}
Run Code Online (Sandbox Code Playgroud)
如何等待BackgroundWorker完成?
在过去,人们尝试过:
while (_worker.IsBusy)
{
Sleep(100);
}
Run Code Online (Sandbox Code Playgroud)
但是这种死锁,因为IsBusy在RunWorkerCompleted事件处理之后才会被清除,并且在应用程序空闲之前无法处理该事件.在工人完成之前,应用程序不会闲置.(另外,这是一个繁忙的循环 - 恶心.)
其他人已添加建议将其融入:
while (_worker.IsBusy)
{
Application.DoEvents();
}
Run Code Online (Sandbox Code Playgroud)
这样做的问题是Application.DoEvents()导致当前队列中的消息被处理,这会导致重入问题(.NET不可重入).
我希望使用一些涉及事件同步对象的解决方案,其中代码等待事件 - 工作者的RunWorkerCompleted事件处理程序设置.就像是:
Event _workerDoneEvent = new WaitHandle();
public void CancelDoingStuff()
{
_worker.CancelAsync();
_workerDoneEvent.WaitOne();
}
private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
_workerDoneEvent.SetEvent();
}
Run Code Online (Sandbox Code Playgroud)
但是我又回到了僵局:事件处理程序在应用程序空闲之前无法运行,并且应用程序不会因为等待事件而空闲.
那么你怎么能等待BackgroundWorker完成呢?
更新 人们似乎对此问题感到困惑.他们似乎认为我将使用BackgroundWorker:
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyWork;
worker.RunWorkerAsync();
WaitForWorkerToFinish(worker);
Run Code Online (Sandbox Code Playgroud)
那不是它,这不是我正在做的事情,这不是在这里被问到的.如果是这种情况,使用后台工作者就没有意义了.
Fre*_*eth 123
如果我理解你的要求是正确的,你可以做这样的事情(代码没有经过测试,但显示了一般的想法):
private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
}
public void Cancel()
{
worker.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(!e.Cancel)
{
// do something
}
_resetEvent.Set(); // signal that worker is done
}
Run Code Online (Sandbox Code Playgroud)
Joe*_*Joe 14
此响应存在问题.UI需要在您等待时继续处理消息,否则它将不会重新绘制,如果您的后台工作程序需要很长时间来响应取消请求,这将是一个问题.
第二个缺陷是,_resetEvent.Set()如果工作线程抛出异常,将永远不会被调用 - 让主线程无限期地等待 - 但是这个缺陷可以通过try/finally块轻松修复.
一种方法是显示一个模态对话框,其中有一个计时器,可以反复检查后台工作程序是否已完成工作(或在您的情况下完成取消).后台工作程序完成后,模态对话框会将控制权返回给您的应用程序.在发生这种情况之前,用户无法与UI进行交互.
另一种方法(假设您最多打开一个无模式窗口)是设置ActiveForm.Enabled = false,然后在Application,DoEvents上循环,直到后台工作程序完成取消,之后您可以再次设置ActiveForm.Enabled = true.
Ian*_*oyd 10
几乎所有人都对这个问题感到困惑,并且不了解工人的使用方式.
考虑RunWorkerComplete事件处理程序:
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
rocketOnPad = false;
label1.Text = "Rocket launch complete.";
}
else
{
rocketOnPad = true;
label1.Text = "Rocket launch aborted.";
}
worker = null;
}
Run Code Online (Sandbox Code Playgroud)
一切都很好.
现在出现呼叫者需要中止倒计时的情况,因为他们需要执行火箭的紧急自毁.
private void BlowUpRocket()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
StartClaxon();
SelfDestruct();
}
Run Code Online (Sandbox Code Playgroud)
还有一种情况是我们需要打开火箭的通道门,但不是在倒计时时:
private void OpenAccessGates()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
if (!rocketOnPad)
DisengageAllGateLatches();
}
Run Code Online (Sandbox Code Playgroud)
最后,我们需要为火箭取消燃料,但在倒计时期间不允许这样做:
private void DrainRocket()
{
if (worker != null)
{
worker.CancelAsync();
WaitForWorkerToFinish(worker);
worker = null;
}
if (rocketOnPad)
OpenFuelValves();
}
Run Code Online (Sandbox Code Playgroud)
如果没有等待worker取消的能力,我们必须将所有三个方法移动到RunWorkerCompletedEvent:
private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
rocketOnPad = false;
label1.Text = "Rocket launch complete.";
}
else
{
rocketOnPad = true;
label1.Text = "Rocket launch aborted.";
}
worker = null;
if (delayedBlowUpRocket)
BlowUpRocket();
else if (delayedOpenAccessGates)
OpenAccessGates();
else if (delayedDrainRocket)
DrainRocket();
}
private void BlowUpRocket()
{
if (worker != null)
{
delayedBlowUpRocket = true;
worker.CancelAsync();
return;
}
StartClaxon();
SelfDestruct();
}
private void OpenAccessGates()
{
if (worker != null)
{
delayedOpenAccessGates = true;
worker.CancelAsync();
return;
}
if (!rocketOnPad)
DisengageAllGateLatches();
}
private void DrainRocket()
{
if (worker != null)
{
delayedDrainRocket = true;
worker.CancelAsync();
return;
}
if (rocketOnPad)
OpenFuelValves();
}
Run Code Online (Sandbox Code Playgroud)
现在我可以像这样写我的代码,但我不会.我不在乎,我不是.
| 归档时间: |
|
| 查看次数: |
77353 次 |
| 最近记录: |