如何正确停止BackgroundWorker
我有一个窗体上有2个组合框。我想根据combobox1.Text
和combobox2.Text
填写combobox2.DataSource
(我假设用户已完成combobox1
的输入,并且正在输入combobox2
的中间)。所以,我对于combobox2
像这样的事件处理程序:如何正确停止BackgroundWorker
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues);
}
至于建筑的DataSource是耗时的过程(它会创建一个请求数据库,并执行它),我决定,这是更好地在另一个进程中执行它使用BackgroundWorker。因此,当cmbDataSourceExtractor尚未完成其工作并且用户再次键入另一个符号时会出现这种情况。在这种情况下,我在此行上发现异常cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues);
有关该BackgroundWorker正忙,无法在同一时间执行多个操作。
如何摆脱这个异常?
在此先感谢!
CancelAsync
实际上并没有中止你的线程或类似的东西。它向工作线程发送消息,通过BackgroundWorker.CancellationPending
应该取消工作。您在后台运行的DoWork委托必须定期检查此属性并处理取消本身。
棘手的部分是你的DoWork委托可能是阻塞,这意味着你在你的数据源做的工作必须完成,然后才能做任何事情(如检查CancellationPending)。您可能需要您的实际工作转移到另一个异步委托(或者更好的是,提交工作到ThreadPool
),和你的主要工作线程轮询直到内部工作线程触发等待状态,或者它检测CancellationPending。
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx
你将不得不使用主线程和BackgroundWorker的之间共享的标志,如BackgroundWorker.CancellationPending
。当你想让BackgroundWorker退出时,只需使用BackgroundWorker.CancelAsync()设置标志。
问题是由以下事实:cmbDataSourceExtractor.CancelAsync()
是一个异步方法造成的,Cancel
操作还未完成时cmdDataSourceExtractor.RunWorkerAsync(...)
exitst。在再次致电RunWorkerAsync
之前,您应该等待cmdDataSourceExtractor
完成。如何解释in this SO question。
如果你喜欢这样就解决您的问题CancelAsync()和的RunWorkerAsync()之间增加一个循环
private void combobox2_TextChanged(object sender, EventArgs e)
{
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues);
}
与调用Application.DoEvents(while循环)将hault的执行你的新工作者线程直到当前的线程被正确取消,记住你仍然需要处理取消你的工作线程。喜欢的东西:
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
的Application.DoEvents()在第一个代码段会继续处理您的GUI线程的消息队列,因此,即使取消和更新cmbDataSourceExtractor.IsBusy属性将被处理(如果你只需添加一个继续而不是应用程序。DoEvents()循环会将GUI线程锁定为繁忙状态,并且不会处理事件以更新cmbDataSourceExtractor.IsBusy)
因为没有Application.DoEvents()在WPF我不能使用此解决方案。当新的请求到达时,工作人员仍然忙时,我设置了附加标志。然后当该标志被设置时,我从RunWorkerCompleted回调的主线程上调用RunWorkerAsync。 – bor 2013-06-05 01:17:57
我同意你们的看法。但有时你必须添加更多的东西。
IE
1)添加这个worker.WorkerSupportsCancellation = true;
2)添加到您类中的一些方法做以下的事情
public void KillMe()
{
worker.CancelAsync();
worker.Dispose();
worker = null;
GC.Collect();
}
所以之前关闭应用程序你必须调用此方法。
3)也许你可以Dispose, null
BackgroundWorker
里面的所有变量和计时器。
看到这里,为什么就没有必要调用Dispose手动一个BackgroundWorker http://*.com/questions/2542326/proper-way-to-dispose-of-a-backgroundworker – 2012-06-21 15:26:38
只是为了节省的Google一对夫妇的调试运行中,如果你的员工有一个循环/或使用RunWorkerCompletedEventHandler并检查取消,你将需要移动处置,空和收集方法进行到RunWorkerCompletedEventHandler。 – HockeyJ 2012-11-27 16:30:16
我的例子。 DoWork的是如下:
DoLengthyWork();
//this is never executed
if(bgWorker.CancellationPending)
{
MessageBox.Show("Up to here? ...");
e.Cancel = true;
}
内DoLenghtyWork:
public void DoLenghtyWork()
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
内OtherStuff():
public void OtherStuff()
{
for(int i=0 ; i<10000000; i++)
{ int j = i/3; }
}
你想要做的是修改DoLenghtyWork和OtherStuff()都让他们成为:
public void DoLenghtyWork()
{
if(!bgWorker.CancellationPending)
{
OtherStuff();
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
public void OtherStuff()
{
if(!bgWorker.CancellationPending)
{
for(int i=0 ; i<10000000; i++)
{
int j = i/3;
}
}
}
在我的情况下,我不得不汇集数据库进行付款确认,然后更新WPF
用户界面。
机制,旋转起来所有的进程:
public void Execute(object parameter)
{
try
{
var amount = ViewModel.Amount;
var transactionId = ViewModel.TransactionMain.TransactionId.ToString();
var productCode = ViewModel.TransactionMain.TransactionDetailList.First().Product.ProductCode;
var transactionReference = GetToken(amount, transactionId, productCode);
var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, transactionReference);
Process.Start(new ProcessStartInfo(url));
ViewModel.UpdateUiWhenDoneWithPayment = new BackgroundWorker {WorkerSupportsCancellation = true};
ViewModel.UpdateUiWhenDoneWithPayment.DoWork += ViewModel.updateUiWhenDoneWithPayment_DoWork;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerCompleted += ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted;
ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerAsync();
}
catch (Exception e)
{
ViewModel.Log.Error("Failed to navigate to payments", e);
MessageBox.Show("Failed to navigate to payments");
}
}
机制,并完成检查:
private void updateUiWhenDoneWithPayment_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(30000);
while (string.IsNullOrEmpty(GetAuthToken()) && !((BackgroundWorker)sender).CancellationPending)
{
Thread.Sleep(5000);
}
//Plug in pooling mechanism
this.AuthCode = GetAuthToken();
}
机制,如果窗口被关闭,其抵消:
private void PaymentView_OnUnloaded(object sender, RoutedEventArgs e)
{
var context = DataContext as PaymentViewModel;
if (context.UpdateUiWhenDoneWithPayment != null && context.UpdateUiWhenDoneWithPayment.WorkerSupportsCancellation && context.UpdateUiWhenDoneWithPayment.IsBusy)
context.UpdateUiWhenDoneWithPayment.CancelAsync();
}
我的答案有点不同,因为我尝试了这些方法,但他们没有奏效。我的代码使用额外的类来检查公共静态类中的布尔标志,因为数据库值被读取,或者在将对象添加到List对象之前我更喜欢它。查看下面代码中的更改。我添加了ThreadWatcher.StopThread属性。对于这个解释,我是nog会恢复当前线程,因为这不是你的问题,但这就像在访问下一个线程之前将属性设置为false一样简单...
private void combobox2_TextChanged(object sender, EventArgs e)
{
//Stop the thread here with this
ThreadWatcher.StopThread = true;//the rest of this thread will run normally after the database function has stopped.
if (cmbDataSourceExtractor.IsBusy)
cmbDataSourceExtractor.CancelAsync();
while(cmbDataSourceExtractor.IsBusy)
Application.DoEvents();
var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
V2 = combobox2.Text};
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues);
}
无一不精
private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
{
if (this.cmbDataSourceExtractor.CancellationPending)
{
e.Cancel = true;
return;
}
// do stuff...
}
现在添加下面的类
public static class ThreadWatcher
{
public static bool StopThread { get; set; }
}
和你的类,你读取数据库
List<SomeObject>list = new List<SomeObject>();
...
if (!reader.IsDbNull(0))
something = reader.getString(0);
someobject = new someobject(something);
if (ThreadWatcher.StopThread == true)
break;
list.Add(something);
...
不要忘记使用一个最后阻止正确关闭你的dat基地连接等希望这有助于!如果您觉得有用,请给我打电话。
我想补充一点,CancelAsync是无阻塞的操作,即使您处理您的DoWork正确CancellationPendning(),你不能继续下一行的DoWork(执行CancelAsync)在几秒钟内可以终止。 – dzendras 2011-01-19 07:36:19