2010-07-14 53 views
57

He buscado en SO y he encontrado respuestas sobre Quartz.net. Pero parece ser demasiado grande para mi proyecto. Quiero una solución equivalente, pero más simple y (en el mejor de) código interno (no se requiere una biblioteca externa). ¿Cómo puedo llamar un método diariamente, en un momento específico?¿Cómo llamar a un método diariamente, en un momento específico, en C#?

tengo que añadir algo de información acerca de esto:

  • la manera más simple (y feo) para hacer esto, es comprobar el tiempo de cada segundo/minuto y llamar al método, en el momento adecuado

Quiero una forma más efectiva de hacer esto, no es necesario verificar constantemente el tiempo, y tengo control sobre si el trabajo está hecho de una manera no efectiva. Si el método falla (debido a algún problema), el programa debe saber escribir para registrar/enviar un correo electrónico. Es por eso que necesito llamar a un método, no programar un trabajo.

Encontré esta solución Call a method at fixed time in Java en Java. ¿Hay alguna manera similar en C#?

EDIT: He hecho esto. Agregué un parámetro en el vacío Principal(), y creé un murciélago (programado por el Programador de tareas de Windows) para ejecutar el programa con este parámetro. El programa se ejecuta, hace el trabajo y luego sale. Si un trabajo falla, es capaz de escribir y enviar correos electrónicos. Este enfoque se ajusta a mis necesidades así :)

+2

Esa pregunta vinculada parece indicar que un método dentro de su aplicación en ejecución tiene que ser llamado periódicamente. Es ese el caso? Impactará si necesita una programación en proceso o si solo puede usar el programador de Windows. – paxdiablo

+0

mi programa, como requisitos, se ejecutará continuamente – Vimvq1987

+0

Oye, no puedo creer que hayas llamado mi respuesta "fea". Las palabras de pelea de ellos :-) – paxdiablo

Respuesta

67
  • Crear una aplicación de consola que hace lo que está buscando
  • Uso de Windows "Scheduled Tasks" funcionalidad para tener esa aplicación consola ejecutada en el momento que lo necesite para funcionar

¡Eso es todo lo que necesitas!

Actualización: si usted quiere hacer esto dentro de su aplicación, tiene varias opciones:

  • en un Windows Forms aplicación, se puede aprovechar el evento Application.Idle y comprobar para ver si Ha llegado el momento del día para llamar a su método. Este método solo se invoca cuando su aplicación no está ocupada con otras cosas. Una comprobación rápida para ver si se ha alcanzado su tiempo objetivo no debe poner demasiado énfasis en su aplicación, creo que ...
  • en una aplicación web ASP.NET, existen métodos para "simular" el envío de eventos programados - echa un vistazo a este CodeProject article
  • y, por supuesto, también puede simplemente "hágalo usted mismo" en cualquier aplicación .NET - echa un vistazo a este CodeProject article para una implementación de ejemplo

actualización # 2: si desea verificar cada 60 minutos, puede crear un temporizador que se active cada 60 minutos y, si se agota el tiempo, llama al método.

Algo como esto:

using System.Timers; 

const double interval60Minutes = 60 * 60 * 1000; // milliseconds to one hour 

Timer checkForTime = new Timer(interval60Minutes); 
checkForTime.Elapsed += new ElapsedEventHandler(checkForTime_Elapsed); 
checkForTime.Enabled = true; 

y luego en el controlador de eventos:

void checkForTime_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    if (timeIsReady()) 
    { 
     SendEmail(); 
    } 
} 
+0

Lo pensé antes. :). Pero mi programa se ejecutará continuamente, y quiero saber una forma alternativa, si hay :) – Vimvq1987

+0

es una aplicación de Winform. Trataré de convencer a mi jefe para que cambie su diseño, pero al principio debería tratar de cumplir con sus requisitos: p – Vimvq1987

+0

Gracias por su respuesta. Agregué algo de información. Me agradezco mucho si pueden ayudar ... – Vimvq1987

4

El mejor método que conozco y, probablemente, el más simple es utilizar el programador de tareas de Windows para ejecutar su codifique en una hora específica del día o haga que su aplicación se ejecute permanentemente y verifique un horario particular del día o escriba un servicio de Windows que haga lo mismo.

1

Si desea que se ejecute un ejecutable, use Windows Scheduled Tasks. Voy a suponer (quizás erróneamente) que desea que se ejecute un método en su programa actual.

¿Por qué no tener un hilo ejecutándose continuamente almacenando la última fecha en que se llamó al método?

Haga que se active cada minuto (por ejemplo) y, si la hora actual es mayor que la hora especificada y la última fecha almacenada no es la fecha actual, llame al método y luego actualice la fecha.

8

Como han dicho otros usuarios, puede utilizar una aplicación de consola para ejecutarla cuando esté programada. Lo que otros no han dicho es que esta aplicación puede desencadenar un proceso cruzado de EventWaitHandle que está esperando en su aplicación principal.

aplicación de consola:

class Program 
{ 
    static void Main(string[] args) 
    { 
     EventWaitHandle handle = 
      new EventWaitHandle(true, EventResetMode.ManualReset, "GoodMutexName"); 
     handle.Set(); 
    } 
} 

Aplicación principal:

private void Form1_Load(object sender, EventArgs e) 
{ 
    // Background thread, will die with application 
    ThreadPool.QueueUserWorkItem((dumby) => EmailWait()); 
} 

private void EmailWait() 
{ 
    EventWaitHandle handle = 
     new EventWaitHandle(false, EventResetMode.ManualReset, "GoodMutexName"); 

    while (true) 
    { 
     handle.WaitOne(); 

     SendEmail(); 

     handle.Reset(); 
    } 
} 
8

Siempre que crean aplicaciones que requieren dicha funcionalidad, siempre use el programador de tareas de Windows a través de una sencilla .NET biblioteca que encontré.

Por favor, see my answer to a similar question para obtener un código de muestra y más explicaciones.

4

Sé que esto es viejo, pero ¿qué tal esto:

Construir un temporizador para disparar en el arranque que calcula el tiempo para el próximo tiempo de ejecución. En la primera llamada del tiempo de ejecución, cancele el primer temporizador y comience un nuevo temporizador diario. cambie cada día a cada hora o lo que quiera que sea la periodicidad.

+8

Y mire si falla durante los cambios de horario de verano. . . –

+1

Buen punto, aunque eso puede ser compensado. – Brent

0

En lugar de establecer un tiempo para ejecutar cada segundo de cada 60 minutos, puede calcular el tiempo restante y establecer el temporizador a la mitad (o alguna otra fracción) de esto. De esta forma, no verificará tanto el tiempo, sino que también mantendrá un grado de autenticidad, ya que el intervalo del temporizador se reduce cuanto más se acerca al tiempo objetivo.

Por ejemplo, si desea hacer algo más de 60 minutos a partir de ahora los intervalos temporizadores serían aproximatly:

30:00:00, 15:00:00, 07:30:00, 03:45:00 , ..., 00:00:01 ¡CORRE!

Utilizo el siguiente código para reiniciar automáticamente un servicio una vez al día. Uso un hilo porque he encontrado que los temporizadores no son confiables durante largos períodos, mientras que esto es más costoso en este ejemplo, es el único creado para este propósito, así que esto no importa.

(conversión de VB.NET)

autoRestartThread = new System.Threading.Thread(autoRestartThreadRun); 
autoRestartThread.Start(); 

...

private void autoRestartThreadRun() 
{ 
    try { 
     DateTime nextRestart = DateAndTime.Today.Add(CurrentSettings.AutoRestartTime); 
     if (nextRestart < DateAndTime.Now) { 
      nextRestart = nextRestart.AddDays(1); 
     } 

     while (true) { 
      if (nextRestart < DateAndTime.Now) { 
       LogInfo("Auto Restarting Service"); 
       Process p = new Process(); 
       p.StartInfo.FileName = "cmd.exe"; 
       p.StartInfo.Arguments = string.Format("/C net stop {0} && net start {0}", "\"My Service Name\""); 
       p.StartInfo.LoadUserProfile = false; 
       p.StartInfo.UseShellExecute = false; 
       p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 
       p.StartInfo.CreateNoWindow = true; 
       p.Start(); 
      } else { 
       dynamic sleepMs = Convert.ToInt32(Math.Max(1000, nextRestart.Subtract(DateAndTime.Now).TotalMilliseconds/2)); 
       System.Threading.Thread.Sleep(sleepMs); 
      } 
     } 
    } catch (ThreadAbortException taex) { 
    } catch (Exception ex) { 
     LogError(ex); 
    } 
} 

Nota He establecer un intervalo de mininum de 1000 ms, esto podría ser increaded, reducir o eliminar dependiendo de la accurcy que requiera

Recuerde también detener su hilo/temporizador cuando se cierre su aplicación.

1

Acabo de escribir recientemente una aplicación C# que tuvo que reiniciar diariamente. Me doy cuenta de que esta pregunta es antigua, pero no creo que me duela agregar otra posible solución. Así es como me las arreglé para reiniciar diariamente a una hora específica.

public void RestartApp() 
{ 
    AppRestart = AppRestart.AddHours(5); 
    AppRestart = AppRestart.AddMinutes(30); 
    DateTime current = DateTime.Now; 
    if (current > AppRestart) { AppRestart = AppRestart.AddDays(1); } 

    TimeSpan UntilRestart = AppRestart - current; 
    int MSUntilRestart = Convert.ToInt32(UntilRestart.TotalMilliseconds); 

    tmrRestart.Interval = MSUntilRestart; 
    tmrRestart.Elapsed += tmrRestart_Elapsed; 
    tmrRestart.Start(); 
} 

para asegurar que su temporizador se mantiene en su alcance recomiendo crear fuera del método utilizando System.Timers.Timer tmrRestart = new System.Timers.Timer() método. Ponga el método RestartApp() en su evento de carga de formularios. Cuando la aplicación se inicia establecerá los valores para AppRestart si current es mayor que el tiempo de reinicio agregamos 1 día a AppRestart para asegurar que el reinicio se realice a tiempo y que no obtengamos una excepción por poner un valor negativo en el temporizador. En el evento tmrRestart_Elapsed, ejecute el código que necesite ejecutar en ese momento específico. Si su aplicación se reinicia por sí misma, no necesariamente tiene que detener el temporizador, pero tampoco está mal. Si la aplicación no se reinicia, simplemente llame de nuevo al método RestartApp() y estará listo.

-1

Podría ser solo yo, pero parecía que la mayoría de estas respuestas no estaban completas o no funcionarían correctamente. Hice algo muy rápido y sucio. Dicho esto, no estoy seguro de cuán buena idea es hacerlo de esta manera, pero funciona perfectamente todo el tiempo.

while (true) 
{ 
    if(DateTime.Now.ToString("HH:mm") == "22:00") 
    { 
     //do something here 
     //ExecuteFunctionTask(); 
     //Make sure it doesn't execute twice by pausing 61 seconds. So that the time is past 2200 to 2201 
     Thread.Sleep(61000); 
    } 

    Thread.Sleep(10000); 
} 
0

Tengo un enfoque simple para esto. Esto crea un retraso de 1 minuto antes de que ocurra la acción. También podría agregar segundos para hacer Thread.Sleep(); corta.

private void DoSomething(int aHour, int aMinute) 
{ 
    bool running = true; 
    while (running) 
    { 
     Thread.Sleep(1); 
     if (DateTime.Now.Hour == aHour && DateTime.Now.Minute == aMinute) 
     { 
      Thread.Sleep(60 * 1000); //Wait a minute to make the if-statement false 
      //Do Stuff 
     } 
    } 
} 
2

Creé un planificador simple que es fácil de usar y no necesita usar una biblioteca externa. TaskScheduler es un singleton que mantiene referencias en los temporizadores para que los temporizadores no sean basura, puede programar múltiples tareas. Puede establecer la primera ejecución (hora y minuto), si en el momento de la programación este tiempo se ha terminado, la programación comenzará el día siguiente, esto en ese momento. Pero es fácil personalizar el código.

Programar una nueva tarea es muy simple. Ejemplo: a las 11:52 la primera tarea es por cada 15 segundos, el segundo ejemplo es por cada 5 segundos. Para la ejecución diaria configure 24 en el parámetro 3.

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00417, 
    () => 
    { 
     Debug.WriteLine("task1: " + DateTime.Now); 
     //here write the code that you want to schedule 
    }); 

TaskScheduler.Instance.ScheduleTask(11, 52, 0.00139, 
    () => 
    { 
     Debug.WriteLine("task2: " + DateTime.Now); 
     //here write the code that you want to schedule 
    }); 

Mi ventana de depuración:

task2: 07.06.2017 11:52:00 
task1: 07.06.2017 11:52:00 
task2: 07.06.2017 11:52:05 
task2: 07.06.2017 11:52:10 
task1: 07.06.2017 11:52:15 
task2: 07.06.2017 11:52:15 
task2: 07.06.2017 11:52:20 
task2: 07.06.2017 11:52:25 
... 

Sólo agregar esta clase a su proyecto:

public class TaskScheduler 
{ 
    private static TaskScheduler _instance; 
    private List<Timer> timers = new List<Timer>(); 

    private TaskScheduler() { } 

    public static TaskScheduler Instance => _instance ?? (_instance = new TaskScheduler()); 

    public void ScheduleTask(int hour, int min, double intervalInHour, Action task) 
    { 
     DateTime now = DateTime.Now; 
     DateTime firstRun = new DateTime(now.Year, now.Month, now.Day, hour, min, 0, 0); 
     if (now > firstRun) 
     { 
      firstRun = firstRun.AddDays(1); 
     } 

     TimeSpan timeToGo = firstRun - now; 
     if (timeToGo <= TimeSpan.Zero) 
     { 
      timeToGo = TimeSpan.Zero; 
     } 

     var timer = new Timer(x => 
     { 
      task.Invoke(); 
     }, null, timeToGo, TimeSpan.FromHours(intervalInHour)); 

     timers.Add(timer); 
    } 
} 
0

Aquí está una manera de hacer esto utilizando TPL.No es necesario crear/eliminar un temporizador, etc.

void ScheduleSomething() 
{ 

    var runAt = DateTime.Today + TimeSpan.FromHours(16); 

    if (runAt <= DateTime.Now) 
    { 
     DoSomething(); 
    } 
    else 
    { 
     var delay = runAt - DateTime.Now; 
     System.Threading.Tasks.Task.Delay(delay).ContinueWith(_ => DoSomething()); 
    } 

} 

void DoSomething() 
{ 
    // do somethig 
} 
Cuestiones relacionadas