C # отмена DoWork фонового рабочего
-
05-07-2019 - |
Вопрос
C# 2008
Я использую приведенный ниже код для входа в программный телефон.Однако процесс входа в систему - это длительный процесс, поскольку есть много вещей, которые необходимо инициализировать и выполнить проверки, я включил здесь лишь некоторые из них, так как это сделало бы публикацию кода слишком длинной.
В приведенном ниже коде я проверяю, была ли вызвана функция CancellationPending if 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";
}
Решение
Возможно, есть только одна проблема:если бы одна из операций в обработчике событий 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 в более удобную форму.
Другие советы
Я считаю, что вы делаете это правильно. Вы найдете членов потока, которые позволяют вам прекратить или прервать поток, но вы не хотите использовать их для чего-то подобного. Может показаться немного странным, что все "отменено" quot; проверяет ваш код, но это позволяет вам точно контролировать выход из потока. Если бы вы были "грубо" прервать рабочий поток, поток не может контролировать, когда он выходит, и может быть повреждено состояние.
В своей функции DoWork () вы написали ...
. В зависимости от того, сколько задач одной и той же структуры поступает, как отображаемые две, вы можете реорганизовать эту структуру в собственный метод, предоставляя изменяющиеся части в качестве параметров.
Также эта ветка InvokeRequired if-else удвоила выходную строку. Небольшой поиск здесь в stackoverflow или в Интернете должен показать вам шаблон для достижения этого удвоения.
Все остальное выглядит неплохо.
Есть одна вещь, которую мне не нужно вызывать this.bgwProcessLogin.CancelAsync (); как вы можете просто установить это e.Cancel = true;