С# отменяет DoWork фонового рабочего

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";
}

person ant2009    schedule 05.05.2009    source источник


Ответы (4)


Проблема может быть только одна: если одна из операций в обработчике событий 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;
        }
    }
}

В этом случае вы действительно можете прервать ожидающие операции, но у вас также есть некоторые ограничения (дополнительную информацию о прерывании управляемого потока и некоторых ограничениях см. в разделе Выявление глубины исключения ThreadAbortException с помощью Rotor).

P.S. Я согласен с Оливером в том, что вы должны обернуть InvokeRequired в более удобную форму.

person Sergey Teplyakov    schedule 29.01.2010
comment
Хороший ответ. Я надеялся, что это будет работать и в Silverlight. Оказывается, это не из-за ограничения безопасности. Вызов Thread.Abort() вызовет исключение MethodAccessException, начиная с Silverlight 4 ( msdn.microsoft.com/en-us/library/ty8d3wta(v=VS.95).aspx). О, это все еще хороший ответ. - person Steve Wortham; 06.10.2011
comment
@SergeyTeplyakov привет, Сергей, у меня точно такой же сценарий (один длительный поток, который я хочу прервать, если нажать кнопку «Отмена»), но я не вижу, как на самом деле отменить его из вашего кода/сообщения. Я новичок в фоновых работах, так что простите мое невежество... - person ganders; 07.05.2012

Вы делаете это правильно, я считаю. Вы найдете членов потока, которые позволяют вам завершить или прервать поток, но вы не хотите использовать их для чего-то подобного. Может показаться немного странным иметь все «отмененные» проверки в вашем коде, но это позволяет вам точно контролировать, когда вы выходите из своего потока. Если бы вы «грубо» прервали рабочий поток, поток не имел бы контроля над тем, когда он завершается, и это могло бы привести к повреждению состояния.

person JMarsch    schedule 05.05.2009

В вашей функции DoWork() вы написали .... В зависимости от того, сколько задач одной и той же структуры будет похоже на отображаемые две, вы можете преобразовать эту структуру в собственный метод, задавая изменяющиеся части в качестве параметров.

Также эта ветвь InvokeRequired if-else удвоила выходную строку. Небольшой поиск здесь, в stackoverflow или в Интернете, должен показать вам шаблон для выполнения этого удвоения.

Все остальное выглядит вполне прилично.

person Oliver    schedule 29.01.2010

Есть одна вещь, которую мне не нужно вызывать this.bgwProcessLogin.CancelAsync(); так как вы можете просто установить это e.Cancel = true;

person ant2009    schedule 06.05.2009