visual tick interval form event elapsedeventhandler dotnetperls cronometro c# timer

tick - timer event c#



C#ejecuta un hilo cada X minutos, pero solo si ese hilo no se está ejecutando ya (17)

¿Por qué no usar un temporizador con Monitor.TryEnter() ? Si se llama nuevamente a OnTimerElapsed() antes de que finalice la OnTimerElapsed() anterior, simplemente se descartará y no se volverá a intentar hasta que el temporizador vuelva a dispararse.

private static readonly object _locker = new object(); private void OnTimerElapsed(object sender, ElapsedEventArgs e) { if (!Monitor.TryEnter(_locker)) { return; } // Don''t let multiple threads in here at the same time. try { // do stuff } finally { Monitor.Exit(_locker); } }

Tengo un programa de C # que necesita enviar un hilo cada X minutos, pero solo si el hilo enviado anteriormente (desde X minutos) no se está ejecutando actualmente .

Un simple Timer antiguo por sí solo no funcionará (porque distribuye un evento cada X minutos independientemente de si el proceso enviado previamente ha finalizado o no).

El proceso que se enviará varía enormemente en el tiempo que lleva realizar su tarea; a veces puede tomar un segundo, a veces puede tomar varias horas. No quiero volver a iniciar el proceso si aún está procesando desde la última vez que se inició.

¿Alguien puede proporcionar algún código de muestra de C # que funcione?


En mi opinión, el camino a seguir en esta situación es usar la clase System.ComponentModel.BackgroundWorker y luego simplemente verificar su propiedad IsBusy cada vez que desee enviar (o no) el nuevo hilo. El código es bastante simple; he aquí un ejemplo:

class MyClass { private BackgroundWorker worker; public MyClass() { worker = new BackgroundWorker(); worker.DoWork += worker_DoWork; Timer timer = new Timer(1000); timer.Elapsed += timer_Elapsed; timer.Start(); } void timer_Elapsed(object sender, ElapsedEventArgs e) { if(!worker.IsBusy) worker.RunWorkerAsync(); } void worker_DoWork(object sender, DoWorkEventArgs e) { //whatever You want the background thread to do... } }

En este ejemplo, utilicé System.Timers.Timer , pero creo que también debería funcionar con otros temporizadores. La clase BackgroundWorker también admite informes de progreso y cancelación, y utiliza un modelo de comunicación impulsado por eventos con el hilo de envío, para que no tenga que preocuparse por variables volátiles y similares ...

EDITAR

Aquí hay un ejemplo más elaborado que incluye cancelación e informes de progreso:

class MyClass { private BackgroundWorker worker; public MyClass() { worker = new BackgroundWorker() { WorkerSupportsCancellation = true, WorkerReportsProgress = true }; worker.DoWork += worker_DoWork; worker.ProgressChanged += worker_ProgressChanged; worker.RunWorkerCompleted += worker_RunWorkerCompleted; Timer timer = new Timer(1000); timer.Elapsed += timer_Elapsed; timer.Start(); } void timer_Elapsed(object sender, ElapsedEventArgs e) { if(!worker.IsBusy) worker.RunWorkerAsync(); } void worker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker w = (BackgroundWorker)sender; while(/*condition*/) { //check if cancellation was requested if(w.CancellationPending) { //take any necessary action upon cancelling (rollback, etc.) //notify the RunWorkerCompleted event handler //that the operation was cancelled e.Cancel = true; return; } //report progress; this method has an overload which can also take //custom object (usually representing state) as an argument w.ReportProgress(/*percentage*/); //do whatever You want the background thread to do... } } void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { //display the progress using e.ProgressPercentage and/or e.UserState } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if(e.Cancelled) { //do something } else { //do something else } } }

Luego, para cancelar la ejecución adicional, simplemente llame a worker.CancelAsync() . Tenga en cuenta que este es un mecanismo de cancelación completamente manejado por el usuario (no es compatible con el aborto de subprocesos ni nada por el estilo desde el primer momento).


Esta pregunta ya tiene una cantidad de buenas respuestas, incluso una más nueva que se basa en algunas de las características del TPL. Pero siento una falta aquí:

  1. La solución basada en TPL a) no está realmente contenida aquí, sino que se refiere a otra respuesta, b) no muestra cómo se podría usar async / await para implementar el mecanismo de tiempo en un único método, yc) la referencia la implementación es bastante complicada, lo que ofusca de algún modo el punto relevante subyacente a esta pregunta en particular.
  2. La pregunta original aquí es algo vaga sobre los parámetros específicos de la implementación deseada (aunque algo de eso se aclara en los comentarios). Al mismo tiempo, otros lectores pueden tener necesidades similares pero no idénticas, y ninguna respuesta aborda la variedad de opciones de diseño que podrían desearse.
  3. Particularmente me gusta implementar un comportamiento periódico usando Task y async / await esta manera, debido a la forma en que simplifica el código. La característica async / await en particular es tan valiosa para tomar un código que de otro modo estaría fracturado por un detalle de implementación de continuación / devolución de llamada, y preservando su lógica lineal natural en un único método. Pero ninguna respuesta aquí demuestra esa simplicidad.

Entonces, con ese razonamiento que me motivó a agregar otra respuesta a esta pregunta ...


Para mí, lo primero a considerar es "¿qué comportamiento exacto se desea aquí?" La pregunta aquí comienza con una premisa básica: que la tarea de período iniciada por el temporizador no debe ejecutarse al mismo tiempo, incluso si la tarea lleva más tiempo que el período del temporizador. Pero hay varias formas en que se puede cumplir esa premisa, que incluyen:

  1. Ni siquiera ejecute el temporizador mientras la tarea se está ejecutando.
  2. Ejecute el temporizador (esta y las opciones restantes que presento aquí presuponen que el temporizador continúa ejecutándose durante la ejecución de la tarea), pero si la tarea tarda más que el período del temporizador, vuelva a ejecutar la tarea inmediatamente después de que se complete desde el tic del temporizador anterior.
  3. Solo inicie la ejecución de la tarea con un tic del temporizador. Si la tarea lleva más tiempo que el período del temporizador, no inicie una nueva tarea mientras se ejecuta la actual, e incluso una vez que la actual se haya completado, no inicie una nueva hasta que el siguiente temporizador marque.
  4. Si la tarea lleva más tiempo que el intervalo del temporizador, no solo ejecutará la tarea nuevamente inmediatamente después de que se complete, sino que la ejecutará tantas veces como sea necesario hasta que la tarea se "atrape". Es decir, con el tiempo, haga un gran esfuerzo para ejecutar la tarea una vez por cada tic del temporizador.

En base a los comentarios, tengo la impresión de que la opción n. ° 3 coincide más con la solicitud original del OP, aunque parece que la opción n. ° 1 posiblemente también funcione. Pero las opciones n. ° 2 y n. ° 4 podrían ser preferibles a otra persona.

En el siguiente ejemplo de código, he implementado estas opciones con cinco métodos diferentes (dos de ellos implementan la opción n. ° 3, pero de maneras ligeramente diferentes). Por supuesto, uno seleccionaría la implementación apropiada para las necesidades de uno. ¡Es probable que no necesites los cinco en un programa! :)

El punto clave es que en todas estas implementaciones, naturalmente y de una manera muy simple, ejecutan la tarea de forma periódica pero no simultánea. Es decir, implementan de manera efectiva un modelo de ejecución basado en temporizador, al tiempo que se aseguran de que la tarea solo se ejecute cada vez por un subproceso a la vez, según la solicitud principal de la pregunta.

Este ejemplo también ilustra cómo se puede utilizar CancellationTokenSource para interrumpir la tarea de período, aprovechando la await de manejar el modelo basado en excepciones de una manera limpia y simple.

class Program { const int timerSeconds = 5, actionMinSeconds = 1, actionMaxSeconds = 7; static Random _rnd = new Random(); static void Main(string[] args) { Console.WriteLine("Press any key to interrupt timer and exit..."); Console.WriteLine(); CancellationTokenSource cancelSource = new CancellationTokenSource(); new Thread(() => CancelOnInput(cancelSource)).Start(); Console.WriteLine( "Starting at {0:HH:mm:ss.f}, timer interval is {1} seconds", DateTime.Now, timerSeconds); Console.WriteLine(); Console.WriteLine(); // NOTE: the call to Wait() is for the purpose of this // specific demonstration in a console program. One does // not normally use a blocking wait like this for asynchronous // operations. // Specify the specific implementation to test by providing the method // name as the second argument. RunTimer(cancelSource.Token, M1).Wait(); } static async Task RunTimer( CancellationToken cancelToken, Func<Action, TimeSpan, Task> timerMethod) { Console.WriteLine("Testing method {0}()", timerMethod.Method.Name); Console.WriteLine(); try { await timerMethod(() => { cancelToken.ThrowIfCancellationRequested(); DummyAction(); }, TimeSpan.FromSeconds(timerSeconds)); } catch (OperationCanceledException) { Console.WriteLine(); Console.WriteLine("Operation cancelled"); } } static void CancelOnInput(CancellationTokenSource cancelSource) { Console.ReadKey(); cancelSource.Cancel(); } static void DummyAction() { int duration = _rnd.Next(actionMinSeconds, actionMaxSeconds + 1); Console.WriteLine("dummy action: {0} seconds", duration); Console.Write(" start: {0:HH:mm:ss.f}", DateTime.Now); Thread.Sleep(TimeSpan.FromSeconds(duration)); Console.WriteLine(" - end: {0:HH:mm:ss.f}", DateTime.Now); } static async Task M1(Action taskAction, TimeSpan timer) { // Most basic: always wait specified duration between // each execution of taskAction while (true) { await Task.Delay(timer); await Task.Run(() => taskAction()); } } static async Task M2(Action taskAction, TimeSpan timer) { // Simple: wait for specified interval, minus the duration of // the execution of taskAction. Run taskAction immediately if // the previous execution too longer than timer. TimeSpan remainingDelay = timer; while (true) { if (remainingDelay > TimeSpan.Zero) { await Task.Delay(remainingDelay); } Stopwatch sw = Stopwatch.StartNew(); await Task.Run(() => taskAction()); remainingDelay = timer - sw.Elapsed; } } static async Task M3a(Action taskAction, TimeSpan timer) { // More complicated: only start action on time intervals that // are multiples of the specified timer interval. If execution // of taskAction takes longer than the specified timer interval, // wait until next multiple. // NOTE: this implementation may drift over time relative to the // initial start time, as it considers only the time for the executed // action and there is a small amount of overhead in the loop. See // M3b() for an implementation that always executes on multiples of // the interval relative to the original start time. TimeSpan remainingDelay = timer; while (true) { await Task.Delay(remainingDelay); Stopwatch sw = Stopwatch.StartNew(); await Task.Run(() => taskAction()); long remainder = sw.Elapsed.Ticks % timer.Ticks; remainingDelay = TimeSpan.FromTicks(timer.Ticks - remainder); } } static async Task M3b(Action taskAction, TimeSpan timer) { // More complicated: only start action on time intervals that // are multiples of the specified timer interval. If execution // of taskAction takes longer than the specified timer interval, // wait until next multiple. // NOTE: this implementation computes the intervals based on the // original start time of the loop, and thus will not drift over // time (not counting any drift that exists in the computer''s clock // itself). TimeSpan remainingDelay = timer; Stopwatch swTotal = Stopwatch.StartNew(); while (true) { await Task.Delay(remainingDelay); await Task.Run(() => taskAction()); long remainder = swTotal.Elapsed.Ticks % timer.Ticks; remainingDelay = TimeSpan.FromTicks(timer.Ticks - remainder); } } static async Task M4(Action taskAction, TimeSpan timer) { // More complicated: this implementation is very different from // the others, in that while each execution of the task action // is serialized, they are effectively queued. In all of the others, // if the task is executing when a timer tick would have happened, // the execution for that tick is simply ignored. But here, each time // the timer would have ticked, the task action will be executed. // // If the task action takes longer than the timer for an extended // period of time, it will repeatedly execute. If and when it // "catches up" (which it can do only if it then eventually // executes more quickly than the timer period for some number // of iterations), it reverts to the "execute on a fixed // interval" behavior. TimeSpan nextTick = timer; Stopwatch swTotal = Stopwatch.StartNew(); while (true) { TimeSpan remainingDelay = nextTick - swTotal.Elapsed; if (remainingDelay > TimeSpan.Zero) { await Task.Delay(remainingDelay); } await Task.Run(() => taskAction()); nextTick += timer; } } }

Una nota final: me encontré con este Q & A después de seguirlo como un duplicado de otra pregunta. En esa otra pregunta, a diferencia de aquí, el OP había notado específicamente que estaban usando la clase System.Windows.Forms.Timer . Por supuesto, esta clase se usa principalmente porque tiene la buena característica de que el evento Tick se plantea en el hilo de la interfaz de usuario.

Ahora, tanto esta como la pregunta implican una tarea que se ejecuta en realidad en una cadena de fondo, por lo que el comportamiento afín de la secuencia de la interfaz de usuario de esa clase de temporizador no es realmente de uso particular en esos escenarios. El código aquí se implementa para que coincida con el paradigma de "iniciar una tarea en segundo plano", pero se puede cambiar fácilmente para que el delegado de taskAction simplemente se invoque directamente, en lugar de ejecutarse en una Task y esperar. Lo bueno de utilizar async / await , además de la ventaja estructural que anoté anteriormente, es que conserva el comportamiento affinitizado de subprocesos que es deseable desde la clase System.Windows.Forms.Timer .


Esto debería hacer lo que quieras. Ejecuta un hilo, luego une el hilo hasta que finaliza. Entra en un ciclo de temporizador para asegurarse de que no está ejecutando un subproceso prematuramente, luego se apaga de nuevo y se ejecuta.

using System.Threading; public class MyThread { public void ThreadFunc() { // do nothing apart from sleep a bit System.Console.WriteLine("In Timer Function!"); Thread.Sleep(new TimeSpan(0, 0, 5)); } }; class Program { static void Main(string[] args) { bool bExit = false; DateTime tmeLastExecuted; // while we don''t have a condition to exit the thread loop while (!bExit) { // create a new instance of our thread class and ThreadStart paramter MyThread myThreadClass = new MyThread(); Thread newThread = new Thread(new ThreadStart(myThreadClass.ThreadFunc)); // just as well join the thread until it exits tmeLastExecuted = DateTime.Now; // update timing flag newThread.Start(); newThread.Join(); // when we are in the timing threshold to execute a new thread, we can exit // this loop System.Console.WriteLine("Sleeping for a bit!"); // only allowed to execute a thread every 10 seconds minimum while (DateTime.Now - tmeLastExecuted < new TimeSpan(0, 0, 10)); { Thread.Sleep(100); // sleep to make sure program has no tight loops } System.Console.WriteLine("Ok, going in for another thread creation!"); } } }

Debería producir algo como:

En la función del temporizador! Dormir un poco! Ok, entrando para otra creación de hilo! En la función del temporizador! Dormir un poco! Ok, entrando para otra creación de hilo! En la función del temporizador! ... ...

¡Espero que esto ayude! SR


Hay al menos 20 formas diferentes de lograr esto, desde el uso de un temporizador y un semáforo, hasta variables volátiles, el uso del TPL, y el uso de una herramienta de programación de código abierto como Quartz, etc.

Crear un hilo es un ejercicio costoso, así que ¿por qué no simplemente crear UNO y dejarlo funcionando en segundo plano, ya que pasará la mayor parte de su tiempo inactivo, no causa un drenaje real en el sistema. Despierte periódicamente y trabaje, luego vuelva a dormir durante el período de tiempo. No importa cuánto tiempo lleve la tarea, siempre esperará al menos el intervalo de tiempo "waitForWork" después de completar antes de comenzar una nueva.

//wait 5 seconds for testing purposes static TimeSpan waitForWork = new TimeSpan(0, 0, 0, 5, 0); static ManualResetEventSlim shutdownEvent = new ManualResetEventSlim(false); static void Main(string[] args) { System.Threading.Thread thread = new Thread(DoWork); thread.Name = "My Worker Thread, Dude"; thread.Start(); Console.ReadLine(); shutdownEvent.Set(); thread.Join(); } public static void DoWork() { do { //wait for work timeout or shudown event notification shutdownEvent.Wait(waitForWork); //if shutting down, exit the thread if(shutdownEvent.IsSet) return; //TODO: Do Work here } while (true); }


Las agallas de esto es el método ExecuteTaskCallback . Este bit está encargado de hacer un poco de trabajo, pero solo si no lo está haciendo ya. Para esto, he usado un ManualResetEvent ( canExecute ) que inicialmente está configurado para ser señalado en el método StartTaskCallbacks .

Tenga en cuenta la forma en que uso canExecute.WaitOne(0) . El cero significa que WaitOne regresará inmediatamente con el estado de WaitHandle ( MSDN ). Si se omite el cero, terminaría con cada llamada a ExecuteTaskCallback eventualmente ejecutando la tarea, lo que podría ser bastante desastroso.

La otra cosa importante es poder terminar el proceso limpiamente. He decidido evitar que el Timer ejecute otros métodos en StopTaskCallbacks porque parece preferible hacerlo mientras que otros trabajos pueden estar en curso. Esto garantiza que no se realizará ningún trabajo nuevo y que la llamada subsiguiente a canExecute.WaitOne(); de hecho cubrirá la última tarea si hay una.

private static void ExecuteTaskCallback(object state) { ManualResetEvent canExecute = (ManualResetEvent)state; if (canExecute.WaitOne(0)) { canExecute.Reset(); Console.WriteLine("Doing some work..."); //Simulate doing work. Thread.Sleep(3000); Console.WriteLine("...work completed"); canExecute.Set(); } else { Console.WriteLine("Returning as method is already running"); } } private static void StartTaskCallbacks() { ManualResetEvent canExecute = new ManualResetEvent(true), stopRunning = new ManualResetEvent(false); int interval = 1000; //Periodic invocations. Begins immediately. Timer timer = new Timer(ExecuteTaskCallback, canExecute, 0, interval); //Simulate being stopped. Timer stopTimer = new Timer(StopTaskCallbacks, new object[] { canExecute, stopRunning, timer }, 10000, Timeout.Infinite); stopRunning.WaitOne(); //Clean up. timer.Dispose(); stopTimer.Dispose(); } private static void StopTaskCallbacks(object state) { object[] stateArray = (object[])state; ManualResetEvent canExecute = (ManualResetEvent)stateArray[0]; ManualResetEvent stopRunning = (ManualResetEvent)stateArray[1]; Timer timer = (Timer)stateArray[2]; //Stop the periodic invocations. timer.Change(Timeout.Infinite, Timeout.Infinite); Console.WriteLine("Waiting for existing work to complete"); canExecute.WaitOne(); stopRunning.Set(); }


Puede deshabilitar y habilitar su temporizador en su devolución de llamada transcurrida.

public void TimerElapsed(object sender, EventArgs e) { _timer.Stop(); //Do Work _timer.Start(); }


Puede detener el temporizador antes de la tarea y volver a iniciarlo después de completar la tarea; esto puede hacer que su toma se realice periódicamente en un intervalo de tiempo par.

public void myTimer_Elapsed(object sender, EventArgs e) { myTimer.Stop(); // Do something you want here. myTimer.Start(); }


Puede usar System.Threading.Timer. El truco es establecer la hora inicial solamente. La hora inicial se establece nuevamente cuando finaliza el intervalo anterior o cuando finaliza el trabajo (esto sucederá cuando el trabajo tarde más tiempo que el intervalo). Aquí está el código de ejemplo.

class Program { static System.Threading.Timer timer; static bool workAvailable = false; static int timeInMs = 5000; static object o = new object(); static void Main(string[] args) { timer = new Timer((o) => { try { if (workAvailable) { // do the work, whatever is required. // if another thread is started use Thread.Join to wait for the thread to finish } } catch (Exception) { // handle } finally { // only set the initial time, do not set the recurring time timer.Change(timeInMs, Timeout.Infinite); } }); // only set the initial time, do not set the recurring time timer.Change(timeInMs, Timeout.Infinite); }


Puede utilizar System.Threading.Timer y simplemente establecer el tiempo de Timeout en Infinite antes de procesar sus datos / método, y cuando se complete reinicie el Timer listo para la próxima llamada.

private System.Threading.Timer _timerThread; private int _period = 2000; public MainWindow() { InitializeComponent(); _timerThread = new System.Threading.Timer((o) => { // Stop the timer; _timerThread.Change(-1, -1); // Process your data ProcessData(); // start timer again (BeginTime, Interval) _timerThread.Change(_period, _period); }, null, 0, _period); } private void ProcessData() { // do stuff; }


Puedes mantener un bool volátil para lograr lo que pediste:

private volatile bool _executing; private void TimerElapsed(object state) { if (_executing) return; _executing = true; try { // do the real work here } catch (Exception e) { // handle your error } finally { _executing = false; } }


Si desea que la devolución de llamada del temporizador se active en una secuencia de fondo, puede usar un System.Threading.Timer . Esta clase de temporizador le permite "Especificar tiempo de Timeout.Infinite para desactivar la señalización periódica". como parte del System.Threading.Timer , lo que provoca que el temporizador se active solo una vez.

Luego puede construir un nuevo temporizador cuando la devolución de llamada de su primer temporizador se dispara y finaliza, lo que impide que se programen varios temporizadores hasta que esté listo para que ocurran.

La ventaja aquí es que no crea temporizadores, luego los cancela repetidamente, ya que nunca programa más que su "próximo evento" a la vez.


Si te entiendo correctamente, en realidad solo quieres asegurarte de que tu hilo no se está ejecutando antes de enviar otro hilo. Digamos que tienes un hilo definido en tu clase como tal .

private System.Threading.Thread myThread;

Tu puedes hacer:

//inside some executed method System.Threading.Timer t = new System.Threading.Timer(timerCallBackMethod, null, 0, 5000);

luego agrega callBack como tal

private void timerCallBackMethod(object state) { if(myThread.ThreadState == System.Threading.ThreadState.Stopped || myThread.ThreadState == System.Threading.ThreadState.Unstarted) { //dispatch new thread } }


Tuve el mismo problema hace un tiempo y todo lo que hice fue utilizar la instrucción lock{} . Con esto, incluso si el temporizador quiere hacer algo, se ve obligado a esperar, hasta el final del bloque de bloqueo.

es decir

lock { // this code will never be interrupted or started again until it has finished }

Esta es una gran manera de estar seguro, su proceso funcionará hasta el final sin interrupción.


Usando la PeriodicTaskFactory de mi publicación here

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); Task task = PeriodicTaskFactory.Start(() => { Console.WriteLine(DateTime.Now); Thread.Sleep(5000); }, intervalInMilliseconds: 1000, synchronous: true, cancelToken: cancellationTokenSource.Token); Console.WriteLine("Press any key to stop iterations..."); Console.ReadKey(true); cancellationTokenSource.Cancel(); Console.WriteLine("Waiting for the task to complete..."); Task.WaitAny(task);

El siguiente resultado muestra que, aunque el intervalo se establece en 1000 milisegundos, cada iteración no se inicia hasta que se completa el trabajo de la acción de la tarea. Esto se logra usando el parámetro synchronous: true opcional.

Press any key to stop iterations... 9/6/2013 1:01:52 PM 9/6/2013 1:01:58 PM 9/6/2013 1:02:04 PM 9/6/2013 1:02:10 PM 9/6/2013 1:02:16 PM Waiting for the task to complete... Press any key to continue . . .

ACTUALIZAR

Si desea el comportamiento de "evento omitido" con PeriodicTaskFactory, simplemente no use la opción síncrona e implemente el Monitor. Pruebe Ingresar como lo hizo Bob aquí https://.com/a/18665948/222434

Task task = PeriodicTaskFactory.Start(() => { if (!Monitor.TryEnter(_locker)) { return; } // Don''t let multiple threads in here at the same time. try { Console.WriteLine(DateTime.Now); Thread.Sleep(5000); } finally { Monitor.Exit(_locker); } }, intervalInMilliseconds: 1000, synchronous: false, cancelToken: cancellationTokenSource.Token);

Lo bueno de PeriodicTaskFactory es que se devuelve una tarea que se puede usar con todas las API de TPL, por ejemplo, Task.Wait , continuations, etc.


¡Allí lo que hice y parece que funciona!

using System; using System.Collections; using System.Linq; using System.Text; using System.Threading; using System.ComponentModel; using System.Timers; namespace Exercice_1_1 { class Program { static void Main(string[] args) { Console.SetWindowPosition(0, 0); Service backgroundWork = new Service(1, 1000); do { } while (!(backgroundWork.ExecutionCNT == 100)); } } class Service { private BackgroundWorker worker; private int executionCNT = 0; public int ExecutionCNT { get { return executionCNT; } } //Constructeur (Compteur du nombre d''execution, Delais entre chaque executions) public Service(int executionCNT, int executionDelay) { this.executionCNT = executionCNT; worker = new BackgroundWorker(); System.Timers.Timer timer = new System.Timers.Timer(executionDelay); worker.DoWork += worker_DoWork; timer.Elapsed += timer_Elapsed; timer.Start(); } void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (!worker.IsBusy) { worker.RunWorkerAsync(); } } void worker_DoWork(object sender, DoWorkEventArgs e) { Console.SetCursorPosition(0, 0); Console.WriteLine("({0})task started..../n/n", executionCNT++); Console.WriteLine("Press any key to end it! OR ctrl + c to end exemple"); Console.ReadLine(); Console.Clear(); } } }


Recomiendo utilizar el temporizador en lugar de hilo, ya que es un objeto más ligero. Para lograr tu objetivo, puedes hacer lo siguiente.

using System.Timers; namespace sample_code_1 { public class ClassName { Timer myTimer; static volatile bool isRunning; public OnboardingTaskService() { myTimer= new Timer(); myTimer.Interval = 60000; myTimer.Elapsed += myTimer_Elapsed; myTimer.Start(); } private void myTimer_Elapsed(object sender, ElapsedEventArgs e) { if (isRunning) return; isRunning = true; try { //Your Code.... } catch (Exception ex) { //Handle Exception } finally { isRunning = false; } } } }

Avíseme si ayuda.