C# 取消后台工作的 DoWork

4

C# 2008

我正在使用以下代码登录软电话。然而,由于有许多需要初始化和检查的内容,登录过程是一个长过程,我仅在此处放了一些内容,因为它会使代码变得太长。

在下面的代码中,我在每个检查之前检查CancellationPending是否已在我的取消按钮单击事件中调用CancelAsync。这样做是否正确?如果检查失败,我还会调用CancelAsync并将e.Cancel设置为true。

我想知道我在这里使用的方法是否是最佳方法。

非常感谢任何建议,

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";
}
4个回答

8
可能只有一个问题:如果DoWork事件处理程序中的某个操作需要很长时间才能完成,那么在此期间您只能在该操作完成后才能中止挂起的操作。如果DoWork事件中的所有操作都不会持续太长时间(例如不超过5秒),那么一切都没问题,但如果其中一个操作需要很长时间(例如5分钟),那么用户必须等待此操作完成。
如果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;
        }
    }
}

在这种情况下,您可以真正中止待处理操作,但是您也有一些限制(有关中止托管线程和一些限制的更多信息,请参见使用Rotor深入研究ThreadAbortException)。
P.S. 我同意Oliver的观点,您应该以更可用的形式包装InvokeRequired。

很好的答案。我也希望这在Silverlight中也能起作用。结果发现由于安全限制,它不能工作。在Silverlight 4中调用Thread.Abort()会抛出MethodAccessException异常(http://msdn.microsoft.com/en-us/library/ty8d3wta(v=VS.95).aspx)。不过,这仍然是一个好答案。 - Steve Wortham
2
@SergeyTeplyakov 你好,我遇到了与您完全相同的情况(我想在单个长时间运行的线程中按取消按钮时中止它),但是我从您的代码/帖子中没有发现如何实际取消它。由于我是后台工作者的新手,请原谅我的无知... - ganders

1
在您的DoWork()函数中,您编写了...。 根据所显示的两个相同结构任务的数量,您可以将此结构重构为自己的方法,并将更改的部分作为参数提供。
此外,如果-else分支已将输出字符串加倍。 在stackoverflow或Web上进行一些搜索应该会向您展示一个实现这种加倍的模式。
其他所有内容看起来都很不错。

1

我相信你正在正确地做。你会发现线程成员可以让你终止或中止一个线程,但你不想在这种情况下使用它们。虽然在代码中加入所有的“取消”检查可能看起来有点奇怪,但这样可以让你精确控制线程何时退出。如果你“粗暴”地中止工作线程,线程就无法控制何时退出,可能会导致状态损坏。


0

有一件事情是不需要呼叫 this.bgwProcessLogin.CancelAsync() 的,因为你可以直接設定為 this e.Cancel = true;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接