C#取消后台工作者的DoWork
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/826096/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# cancelling DoWork of background worker
提问by ant2009
C# 2008
C# 2008
I am using the code below to login to a softphone. However, the login progess is a long process as there are many things that have to be initialized and checks to be made, I have only put a few on here, as it would make the code to long to post.
我正在使用下面的代码登录到软电话。然而,登录过程是一个漫长的过程,因为有很多东西需要初始化和检查,我在这里只放了一些,因为它会使代码很长才能发布。
In the code below I am checking if the CancellationPending if the CancelAsync has been called in my cancel button click event, before doing each check. Is this correct? Also if the check fails I also call the CancelAsync and set the e.Cancel to true.
在下面的代码中,我正在检查 CancellationPending 是否在我的取消按钮单击事件中调用了 CancelAsync,然后再进行每次检查。这样对吗?此外,如果检查失败,我也会调用 CancelAsync 并将 e.Cancel 设置为 true。
I would like to know if my method I have used here is the best method to use.
我想知道我在这里使用的方法是否是最好的方法。
Many thanks for any advice,
非常感谢您的任何建议,
private void bgwProcessLogin_DoWork(object sender, DoWorkEventArgs e)
{
/*
* Perform at test to see if the background worker has been
* cancelled by the user before attemping to continue to login.
*
* Cancel background worker on any failed attemp to login
*/
// Start with cancel being false as to reset this if cancel has been set to true
// in the cancel button.
e.Cancel = false;
NetworkingTest connection_test = new NetworkingTest();
if (!this.bgwProcessLogin.CancellationPending)
{
// Check local LAN or Wireless connection
if (!connection_test.IsNetworkConnected())
{
// Update label
if (this.lblRegistering.InvokeRequired)
{
this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "No network connection");
}
else
{
this.lblRegistering.Text = "No network connection";
}
// Failed attemp
this.bgwProcessLogin.CancelAsync();
e.Cancel = true;
return;
}
// Report current progress
this.bgwProcessLogin.ReportProgress(0, "Network connected");
}
else
{
// User cancelled
e.Cancel = true;
return;
}
// Test if access to Server is available
if (!this.bgwProcessLogin.CancellationPending)
{
if (!connection_test.IsSIPServerAvailable())
{
// Update label
if (this.lblRegistering.InvokeRequired)
{
this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "Server unavailable");
}
else
{
this.lblRegistering.Text = "Server unavailable";
}
// Failed attemp
this.bgwProcessLogin.CancelAsync();
e.Cancel = true;
return;
}
// Report current progress
this.bgwProcessLogin.ReportProgress(1, "Server available");
}
else
{
// User cancelled
e.Cancel = true;
return;
}
.
.
.
}
private void bgwProcessLogin_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Check for any errors
if (e.Error == null)
{
if (e.Cancelled)
{
// User cancelled login or login failed
}
else
{
// Login completed successfully
}
}
else
{
// Something failed display error
this.statusDisplay1.CallStatus = e.Error.Message;
}
}
private void bgwProcessLogin_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.lblRegistering.Text = e.UserState.ToString();
}
private void btnCancel_Click(object sender, EventArgs e)
{
// Cancel the logging in process
this.bgwProcessLogin.CancelAsync();
this.lblRegistering.Text = "Logged out";
}
采纳答案by Sergey Teplyakov
There is maybe only one problem: if one of the operation in DoWork event handler would last for a long time. In this case you could abort your pending operation ONLY after that operation finished. If all operations in DoWork event can't last very long (for instance, no more than 5 seconds), its all OK, but if one of the operations can last for long time (5 minutes, for instance) in this case user have to wait until this operation finished.
可能只有一个问题:如果 DoWork 事件处理程序中的某个操作会持续很长时间。在这种情况下,您只能在该操作完成后中止挂起的操作。如果 DoWork 事件中的所有操作都不能持续很长时间(例如,不超过 5 秒),则一切正常,但是如果其中一个操作可以持续很长时间(例如,5 分钟),在这种情况下用户有等待此操作完成。
If DoWork contains long lasting operations you can use something like AbortableBackgroundWorker. Something like this:
如果 DoWork 包含持久操作,您可以使用类似 AbortableBackgroundWorker 的东西。像这样的东西:
public class AbortableBackgroundWorker : BackgroundWorker
{
private Thread workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
workerThread = Thread.CurrentThread;
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
public void Abort()
{
if (workerThread != null)
{
workerThread.Abort();
workerThread = null;
}
}
}
In this case you can truly abort pending operations, but you also have some restrictions (for more information about aborting managed thread and some restrictions see Plumbing the Depths of the ThreadAbortException Using Rotor).
在这种情况下,您可以真正中止挂起的操作,但您也有一些限制(有关中止托管线程和一些限制的更多信息,请参阅使用 Rotor 检测 ThreadAbortException 的深度)。
P.S. I agree with Oliver that you should wrap InvokeRequired in more usable form.
PS 我同意 Oliver 的观点,您应该以更实用的形式包装 InvokeRequired。
回答by JMarsch
You are doing it the right way, I believe. You will find thread members that allow you to terminate or abort a thread, but you don't want to use them for something like this. It might look a little weird to have all of the "cancelled" checks in your code, but that allows you to control exactly when you exit your thread. If you were to "rudely" abort the worker thread, the thread has no control of when it exits, and there could be corrupted state.
我相信,你的做法是正确的。您会发现线程成员允许您终止或中止线程,但您不想将它们用于类似的事情。在您的代码中包含所有“已取消”的检查可能看起来有点奇怪,但这使您可以准确控制何时退出线程。如果您“粗鲁地”中止工作线程,则该线程无法控制它何时退出,并且可能会出现损坏状态。
回答by ant2009
There is one thing I don't need to call the this.bgwProcessLogin.CancelAsync(); as you can just set this e.Cancel = true;
有一件事我不需要调用 this.bgwProcessLogin.CancelAsync(); 因为你可以设置这个 e.Cancel = true;
回答by Oliver
Within your DoWork() function you wrote ...
. Depending on how many tasks of the same structure are coming like the displayed two one, you could refactor this structure into an own method, giving the changing parts as parameters.
在您编写的 DoWork() 函数中...
。根据显示的两个相同结构的任务有多少,您可以将此结构重构为自己的方法,将变化的部分作为参数。
Also this InvokeRequired if-else branch has doubled the output string. A little search here on stackoverflow or on the web should show you a pattern to accomplish this doubling.
此外,这个 InvokeRequired if-else 分支使输出字符串加倍。在 stackoverflow 或网络上进行一些搜索应该会向您展示实现这种加倍的模式。
Evernything else looks quite good.
其他一切看起来都很好。