I made a process that transfers data to the DB and must run in the background, so that the user can pause the process or cancel it at any time.
I did it using the tool provided by Visual Studio C# "BackgoundWorker", this way it works, but I want to optimize it (Improve it):
How can I improve this process, is there a better method?
In the DoWork event of the BackgoundWorker I have the following:
private void corriendo_proceso(object sender, DoWorkEventArgs e)
{
try
{
//Cuando se preciona en detener vuelve esta valor verdadero.
if (pausar_proceso == true)
{
//Este ciclo es quien pausa el proceso hasta que el usuario responda Si o NO
while (pausar_proceso == true)
{
//Cuando el ussuario no desea cancelar y preciona que NO
if (pausar_proceso == false)
{
break; //Rompe el ciclo While
}
//Cuando el usaurio Cancela y preciona que SI
if (proceso_segundo_plano.CancellationPending)
{
e.Cancel = true;
break;
}
}
}
else //Continuar el proceso...
{
Thread.Sleep(0010); //Tiempo de espera.
//Ejecuta todo el proceso y tareas de segundo plano....
}
}
catch (SqlException excepcion)
{
MessageBox.Show("Ha ocurrido un error en el proceso. \n\nDetalles del error: \n\n" + excepcion.Message,
"AVISO", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return;
}
In the RunWorkerCompleted event:
private void proceso_completo(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//Mensaje de Error.
}
else
{
if (e.Cancelled) //Si se cancelo el proceso.
{
if (pausado == true)
{
//Mensaje de espera.
pausado = false;
}
else
{
//Mensaje de Cancelado.
}
}
else
{
//Mensaje de Completado
}
}
return;
}
In the ProgressChanged event:
private void progreso_proceso(object sender, ProgressChangedEventArgs e)
{
// Modificar objetos que son creados en otro hilo.
// y para disparar un evento en este metodo para notificar a otros metodos o clases
//....
return;
}
Button that executes the process:
private void boton_ejecutar_proceso_Click(object sender, EventArgs e)
{
//Inicializando valores de las variables.
pausar_proceso = false;
//Ininicializar los eventos.
proceso_segundo_plano = null;
proceso_segundo_plano = new BackgroundWorker();
proceso_segundo_plano.WorkerReportsProgress = true;
proceso_segundo_plano.WorkerSupportsCancellation = true;
proceso_segundo_plano.DoWork += new DoWorkEventHandler(corriendo_proceso);
proceso_segundo_plano.ProgressChanged += new ProgressChangedEventHandler(progreso_proceso);
proceso_segundo_plano.RunWorkerCompleted += new RunWorkerCompletedEventHandler(proceso_completo);
//Invocar el proceso.
proceso_segundo_plano.RunWorkerAsync();
}
Stop process button:
private void boton_detener_Click(object sender, EventArgs e)
{
if (proceso_segundo_plano.IsBusy)
{
//Mensaje Esperando por el usuario.
pausar_proceso = true; // esto activará el evento de "Dowork" para hacer un bucle que
//que comprueba si el valor de "pausar_proceso" está falso
DialogResult resultado;
resultado = MessageBox.Show("¿Esta seguro que desea detener el Proceso?", "DETENER", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (resultado == DialogResult.Yes)
{
pausar_proceso = true;
cancelado = true;
proceso_segundo_plano.CancelAsync();
//Proceso Cancelado.
}
else
{
//Continuar con el proceso...
pausar_proceso = false; //si el usuario hace clic en NO continuará el proceso.
return;
}
}
}
Previously I did it using "ThreadStart", but Visual Studio showed me exceptions that the method is deprecated.
ThreadStart delegate = new ThreadStart(RunProcess);
Thread thread = new Thread(delegate);
thread.Start();
thread.Resume();
thread.Pause();
thread.Abort();
NOTE: I don't have much experience doing background processes.
I wrote some rudimentary code that demonstrates the use of
await
/Task
with a non-blocking UI.Some code excerpts (for example the pause token) are taken from other places. At the end are the references of what corresponds.
The complete code is on GitHub . Here I will explain only the important parts.
(The test includes a nuget mahapps package, it is to use the wait control and notice that something happens on the screen).
Await is the operator used to execute asynchronous calls, while the Task class is used to define the methods to execute asynchronously.
Suppose we have the following process
This process is asynchronous, and executes a call to sleep, which does nothing more than sleep a thread, which due to the characteristics of Task.Run, is a child thread.
This method is the click event of a button. was defined as async because the compiler forces you to add this information to the events when they are going to make use of a call
await
.await Prueba1()
It executes asynchronously, that means it won't block the UI (you can still move the screen without problem) and waits for the called process to finish before continuing with the execution of the button. This is so because the compiler builds a state machine to make it work.To generate a cancel and/or suspend and resume effect, you have to complicate the code a bit more:
This process admits cancellations and suspensions. The suspension token is copied from this blog
The cancellation token is the original of the CancellationToken class.
This process executes the function described above. However, it can be canceled by running:
or suspended/resumed using:
These objects are defined at the class level as: