System.Timers.Timer 的问题。偶尔发射不止一次
Problems with System.Timers.Timer. Firing more than once occasionally
我正在使用 System.Timers.Timer 每天备份我的 SQL Server Express 数据库一次。它似乎工作正常,大部分时间。有时,ElapsedEventHandler 会以 1 或 4 分钟的间隔被调用多次。它应该每天被击中一次。我将 AutoReset 设置为 false,并在 ElapsedEventHandler 的末尾调用 Start。此外,可能相关的是我重新计算了时间间隔,以便计时器始终在接近凌晨 1 点时关闭。尽可能。数据库的备份可能需要几分钟,如果我不更改间隔,时间可能会漂移得令人无法接受。我提到这一点是因为这些链接表明重置间隔可能存在问题:
- Thread-safety of System.Timers.Timer vs System.Threading.Timer
- Multiple timer elapsed issue
具体请参阅 Hans Passant 的回答
但是,我不知道如何避免重置间隔。另外,我查看了 System.Timers.Timer 的代码。似乎只是重置间隔不会再次启动计时器。我不反对使用不同的计时器 (System.Threading.Timer?),但我想先知道发生了什么。
我已经粘贴了下面的所有代码。我认为真正相关的部分是方法:DatabaseBackupTimerOnElapsed
最后,我会提到程序有时会停止并重新启动(如果代码的其他部分存在未捕获的异常)。我会假设即使没有调用 Dispose,所有计时器在退出程序时都会被终止?那就是定时器不存在于操作系统中?
编辑
我被要求写下一个小的、完整的、可验证的例子。我在这里这样做。我保留了完整的示例,因为有人可能会声称(非常正确!)我删除了一个重要的细节。我已经 运行 这段代码并没有发现问题,但是,它只是偶尔发生在原始代码中。
public class DatabaseCleanupManager
{
private const int MaxRetries = 5;
private const int DatabaseBackupHourOneAm = 1;
private Timer _databaseBackupTimer;
public DatabaseCleanupManager()
{ }
public void Initialize()
{
Console.WriteLine("Initialize");
TimeSpan spanTimer = GetDBBackupTimeSpan(1);
_databaseBackupTimer = new Timer(spanTimer.TotalMilliseconds)
{
AutoReset = false,
};
_databaseBackupTimer.Elapsed += DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Start();
}
private TimeSpan GetDBBackupTimeSpan(int databaseBackupFrequencyInDays)
{
Console.WriteLine("GetDBBackupTimeSpan");
DateTime dt1 = DateTime.Now;
DateTime dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
// I'm really interested in a timer once a day. I'm just trying to get it to happen quicker!
//dt2 = dt2.AddDays(databaseBackupFrequencyInDays);
dt2 = dt1.AddMinutes(4);
TimeSpan spanTimer = dt2 - dt1;
if (spanTimer.TotalMilliseconds < 0) // This could conceivably happen if the have 0 or a negative number (erroneously) for DatabaseBackupFrequencyInDays
{
dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
//dt2 = dt2.AddDays(databaseBackupFrequencyInDays);
dt2 = dt1.AddMinutes(4);
spanTimer = dt2 - dt1;
}
return spanTimer;
}
public void PerformDatabaseMaintenance()
{
if (BackupCurrentDatabase())
{
var success = CleanupExpiredData();
if (success)
{
Console.WriteLine("Database Maintenance Finished");
}
}
}
public void Dispose()
{
_databaseBackupTimer.Elapsed -= DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Stop();
_databaseBackupTimer.Dispose();
}
private void DatabaseBackupTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
try
{
Console.WriteLine("DatabaseBackupTimerOnElapsed at: " + DateTime.Now);
PerformDatabaseMaintenance();
TimeSpan spanTimer = GetDBBackupTimeSpan(1);
// NOTICE I'm calculating Interval again. Some posts suggested that this restarts timer
_databaseBackupTimer.Interval = Math.Max(spanTimer.TotalMilliseconds, TimeSpan.FromMinutes(1).TotalMilliseconds);
_databaseBackupTimer.Start();
}
catch (Exception )
{
// something went wrong - log problem and start timer again.
_databaseBackupTimer.Start();
}
}
private bool BackupCurrentDatabase()
{
// actually backup database but here I'll just sleep for 1 minute...
Thread.Sleep(1000);
Console.WriteLine("Backed up DB at: " + DateTime.Now);
return true;
}
private bool CleanupExpiredData()
{
// Actually remove old SQL Server Express DB .bak files but here just sleep
Thread.Sleep(1000);
Console.WriteLine("Cleaned up old .Bak files at: " + DateTime.Now);
return true;
}
}
class Program
{
static void Main(string[] args)
{
DatabaseCleanupManager mgr = new DatabaseCleanupManager();
mgr.Initialize();
// here we'd normally be running other threads etc., but for here...
Thread.Sleep(24*60*60*1000); // sleep for 1 day
}
}
结束编辑
public class DatabaseCleanupManager : IDatabaseCleanupManager
{
private const int MaxRetries = 5;
private const int DatabaseBackupHourOneAm = 1;
private readonly ISystemConfiguration _systemConfiguration;
private readonly IPopsicleRepository _repository;
private readonly ISystemErrorFactory _systemErrorFactory;
private readonly IAuthorizationManager _authorizationManager;
private readonly IReportRobotState _robotStateReporter;
private Timer _databaseBackupTimer;
public DatabaseCleanupManager(
IPopsicleRepository repository,
ISystemConfiguration configuration,
ISystemErrorFactory systemErrorFactory,
IAuthorizationManager authorizationManager,
IReportRobotState robotStateReporter)
{
if (repository == null)
throw new ArgumentNullException("repository");
if (configuration == null)
throw new ArgumentNullException("configuration");
if (systemErrorFactory == null)
throw new ArgumentNullException("systemErrorFactory");
if (authorizationManager == null)
throw new ArgumentNullException("authorizationManager");
if (robotStateReporter == null)
throw new ArgumentNullException("robotStateReporter");
_repository = repository;
_systemConfiguration = configuration;
_systemErrorFactory = systemErrorFactory;
_authorizationManager = authorizationManager;
_robotStateReporter = robotStateReporter;
}
public event EventHandler<SystemErrorEventArgs> SystemError;
public event EventHandler<SystemErrorClearedEventArgs> SystemErrorCleared;
public void Initialize()
{
TimeSpan spanTimer = GetDBBackupTimeSpan(_systemConfiguration.DatabaseBackupFrequencyInDays);
_databaseBackupTimer = new Timer(spanTimer.TotalMilliseconds)
{
AutoReset = false,
};
_databaseBackupTimer.Elapsed += DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Start();
}
private TimeSpan GetDBBackupTimeSpan(int databaseBackupFrequencyInDays)
{
DateTime dt1 = DateTime.Now;
DateTime dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
dt2 = dt2.AddDays(_systemConfiguration.DatabaseBackupFrequencyInDays);
TimeSpan spanTimer = dt2 - dt1;
if (spanTimer.TotalMilliseconds < 0) // This could conceivably happen if the have 0 or a negative number (erroneously) for DatabaseBackupFrequencyInDays in configuration.json
{
dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
dt2 = dt2.AddDays(1);
spanTimer = dt2 - dt1;
}
return spanTimer;
}
public void PerformDatabaseMaintenance()
{
if (BackupCurrentDatabase())
{
var success = CleanupExpiredData();
if (success)
{
Logger.Log(LogLevel.Info, string.Format("Database Maintenance succeeded"));
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupComplete, "Database backup completed");
}
}
}
public void Dispose()
{
_databaseBackupTimer.Elapsed -= DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Stop();
_databaseBackupTimer.Dispose();
}
private void DatabaseBackupTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
try
{
PerformDatabaseMaintenance();
TimeSpan spanTimer = GetDBBackupTimeSpan(_systemConfiguration.DatabaseBackupFrequencyInDays);
_databaseBackupTimer.Interval = Math.Max(spanTimer.TotalMilliseconds, TimeSpan.FromMinutes(10).TotalMilliseconds);
_databaseBackupTimer.Start();
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning,
string.Format("Database Backup Failed: {0}, ",
e.Message));
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed,
"Database backup failed ");
_databaseBackupTimer.Start();
}
}
private bool BackupCurrentDatabase()
{
try
{
_repository.Alerts.Count();
}
catch (Exception ex)
{
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed, "Database backup failed - the database server does not respond or the database does not exist");
throw new InvalidOperationException(string.Format("The DB does not exist : {0} Error {1}", _systemConfiguration.LocalDbPath, ex.Message));
}
if (!Directory.Exists(_systemConfiguration.LocalBackupFolderPath))
Directory.CreateDirectory(_systemConfiguration.LocalBackupFolderPath);
var tries = 0;
var success = false;
while (!success && tries < MaxRetries)
{
try
{
_repository.BackupDatabase(_systemConfiguration.LocalBackupFolderPath);
success = true;
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning, string.Format("Database Backup Failed: {0}, retrying backup", e.Message));
Thread.Sleep(TimeSpan.FromSeconds(1));
tries++;
if (tries == MaxRetries)
{
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed, string.Format("Database backup failed - {0}", e.Message));
}
}
}
var backupDirectory = new DirectoryInfo(_systemConfiguration.LocalBackupFolderPath);
var files = backupDirectory.GetFiles().OrderBy(f => f.CreationTime).ToArray();
if (files.Length > _systemConfiguration.MaxDatabaseBackups)
{
for (var i = 0; i < (files.Length - _systemConfiguration.MaxDatabaseBackups); i++)
{
try
{
files[i].Delete();
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning, string.Format("Failed to delete old backup: {0}", e.Message));
}
}
}
Logger.Log(LogLevel.Info, success ?
"Database Backup succeeded" :
string.Format("Database Backup failed after {0} retries", MaxRetries));
return success;
}
private bool CleanupExpiredData()
{
var success = false;
try
{
var expirationTime = DateTime.Now - TimeSpan.FromDays(_systemConfiguration.DatabaseDataExpirationInDays);
_repository.DeleteTemperatureReadingsBeforeDate(expirationTime);
_repository.DeleteTransactionsBeforeDate(expirationTime);
success = true;
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning, string.Format("Failed to cleanup expired data: {0}", e.Message));
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed, string.Format("Database cleanup of expired data failed - {0}", e.Message));
}
Logger.Log(LogLevel.Info, success ?
string.Format("Database clean up expired data succeeded") :
string.Format("Database clean up expired data failed"));
return success;
}
private void NotifySystemError(ErrorLevel errorLevel, ErrorCode errorCode, string description)
{
var handler = SystemError;
if (handler != null)
{
var systemError = _systemErrorFactory.CreateSystemError(errorLevel, errorCode, description);
handler(this, new SystemErrorEventArgs(systemError));
}
}
}
我认为解决方案过于复杂。
- 计时器间隔将触发 Elapsed 事件,而不管前一个 运行 是否已完成,除非您在事件期间明确停止计时器。这应该没有必要。当您输入该方法时,您可以简单地跟踪您是否 运行ning。
将间隔设置为59999。这是一分钟不到的毫秒。然后在事件处理程序的入口处检查当前的小时和分钟是否对应于您要备份的时间。
private bool running = false;
private Timer timer = new Timer();
//other code.
private void Initialize()
{
timer.Interval = 59999;
myTimer.Elapsed += TimerElapsed;
timer.Start();
}
public void TimerElapsed(object sender, ElapsedEventArgs e)
{
if (running) return;
DateTime dt = DateTime.Now();
if (!(dt.Hour.Equals(1) && dt.Minute.Equals(0))) return;
running = true;
//other code
running = false;
}
此外,我会在配置文件或注册表中保留 运行备份的时间,这样如果我想更改它,我可以在不重新编译我的服务的情况下进行。
我怀疑 Initialize
被多次调用,您可以做的是在创建新实例之前检查 _databaseBackupTimer
是否为 null
如果它不为空,则跳过该方法中的整个代码
我正在使用 System.Timers.Timer 每天备份我的 SQL Server Express 数据库一次。它似乎工作正常,大部分时间。有时,ElapsedEventHandler 会以 1 或 4 分钟的间隔被调用多次。它应该每天被击中一次。我将 AutoReset 设置为 false,并在 ElapsedEventHandler 的末尾调用 Start。此外,可能相关的是我重新计算了时间间隔,以便计时器始终在接近凌晨 1 点时关闭。尽可能。数据库的备份可能需要几分钟,如果我不更改间隔,时间可能会漂移得令人无法接受。我提到这一点是因为这些链接表明重置间隔可能存在问题:
- Thread-safety of System.Timers.Timer vs System.Threading.Timer
- Multiple timer elapsed issue
具体请参阅 Hans Passant 的回答
但是,我不知道如何避免重置间隔。另外,我查看了 System.Timers.Timer 的代码。似乎只是重置间隔不会再次启动计时器。我不反对使用不同的计时器 (System.Threading.Timer?),但我想先知道发生了什么。
我已经粘贴了下面的所有代码。我认为真正相关的部分是方法:DatabaseBackupTimerOnElapsed
最后,我会提到程序有时会停止并重新启动(如果代码的其他部分存在未捕获的异常)。我会假设即使没有调用 Dispose,所有计时器在退出程序时都会被终止?那就是定时器不存在于操作系统中?
编辑 我被要求写下一个小的、完整的、可验证的例子。我在这里这样做。我保留了完整的示例,因为有人可能会声称(非常正确!)我删除了一个重要的细节。我已经 运行 这段代码并没有发现问题,但是,它只是偶尔发生在原始代码中。
public class DatabaseCleanupManager
{
private const int MaxRetries = 5;
private const int DatabaseBackupHourOneAm = 1;
private Timer _databaseBackupTimer;
public DatabaseCleanupManager()
{ }
public void Initialize()
{
Console.WriteLine("Initialize");
TimeSpan spanTimer = GetDBBackupTimeSpan(1);
_databaseBackupTimer = new Timer(spanTimer.TotalMilliseconds)
{
AutoReset = false,
};
_databaseBackupTimer.Elapsed += DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Start();
}
private TimeSpan GetDBBackupTimeSpan(int databaseBackupFrequencyInDays)
{
Console.WriteLine("GetDBBackupTimeSpan");
DateTime dt1 = DateTime.Now;
DateTime dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
// I'm really interested in a timer once a day. I'm just trying to get it to happen quicker!
//dt2 = dt2.AddDays(databaseBackupFrequencyInDays);
dt2 = dt1.AddMinutes(4);
TimeSpan spanTimer = dt2 - dt1;
if (spanTimer.TotalMilliseconds < 0) // This could conceivably happen if the have 0 or a negative number (erroneously) for DatabaseBackupFrequencyInDays
{
dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
//dt2 = dt2.AddDays(databaseBackupFrequencyInDays);
dt2 = dt1.AddMinutes(4);
spanTimer = dt2 - dt1;
}
return spanTimer;
}
public void PerformDatabaseMaintenance()
{
if (BackupCurrentDatabase())
{
var success = CleanupExpiredData();
if (success)
{
Console.WriteLine("Database Maintenance Finished");
}
}
}
public void Dispose()
{
_databaseBackupTimer.Elapsed -= DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Stop();
_databaseBackupTimer.Dispose();
}
private void DatabaseBackupTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
try
{
Console.WriteLine("DatabaseBackupTimerOnElapsed at: " + DateTime.Now);
PerformDatabaseMaintenance();
TimeSpan spanTimer = GetDBBackupTimeSpan(1);
// NOTICE I'm calculating Interval again. Some posts suggested that this restarts timer
_databaseBackupTimer.Interval = Math.Max(spanTimer.TotalMilliseconds, TimeSpan.FromMinutes(1).TotalMilliseconds);
_databaseBackupTimer.Start();
}
catch (Exception )
{
// something went wrong - log problem and start timer again.
_databaseBackupTimer.Start();
}
}
private bool BackupCurrentDatabase()
{
// actually backup database but here I'll just sleep for 1 minute...
Thread.Sleep(1000);
Console.WriteLine("Backed up DB at: " + DateTime.Now);
return true;
}
private bool CleanupExpiredData()
{
// Actually remove old SQL Server Express DB .bak files but here just sleep
Thread.Sleep(1000);
Console.WriteLine("Cleaned up old .Bak files at: " + DateTime.Now);
return true;
}
}
class Program
{
static void Main(string[] args)
{
DatabaseCleanupManager mgr = new DatabaseCleanupManager();
mgr.Initialize();
// here we'd normally be running other threads etc., but for here...
Thread.Sleep(24*60*60*1000); // sleep for 1 day
}
}
结束编辑
public class DatabaseCleanupManager : IDatabaseCleanupManager
{
private const int MaxRetries = 5;
private const int DatabaseBackupHourOneAm = 1;
private readonly ISystemConfiguration _systemConfiguration;
private readonly IPopsicleRepository _repository;
private readonly ISystemErrorFactory _systemErrorFactory;
private readonly IAuthorizationManager _authorizationManager;
private readonly IReportRobotState _robotStateReporter;
private Timer _databaseBackupTimer;
public DatabaseCleanupManager(
IPopsicleRepository repository,
ISystemConfiguration configuration,
ISystemErrorFactory systemErrorFactory,
IAuthorizationManager authorizationManager,
IReportRobotState robotStateReporter)
{
if (repository == null)
throw new ArgumentNullException("repository");
if (configuration == null)
throw new ArgumentNullException("configuration");
if (systemErrorFactory == null)
throw new ArgumentNullException("systemErrorFactory");
if (authorizationManager == null)
throw new ArgumentNullException("authorizationManager");
if (robotStateReporter == null)
throw new ArgumentNullException("robotStateReporter");
_repository = repository;
_systemConfiguration = configuration;
_systemErrorFactory = systemErrorFactory;
_authorizationManager = authorizationManager;
_robotStateReporter = robotStateReporter;
}
public event EventHandler<SystemErrorEventArgs> SystemError;
public event EventHandler<SystemErrorClearedEventArgs> SystemErrorCleared;
public void Initialize()
{
TimeSpan spanTimer = GetDBBackupTimeSpan(_systemConfiguration.DatabaseBackupFrequencyInDays);
_databaseBackupTimer = new Timer(spanTimer.TotalMilliseconds)
{
AutoReset = false,
};
_databaseBackupTimer.Elapsed += DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Start();
}
private TimeSpan GetDBBackupTimeSpan(int databaseBackupFrequencyInDays)
{
DateTime dt1 = DateTime.Now;
DateTime dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
dt2 = dt2.AddDays(_systemConfiguration.DatabaseBackupFrequencyInDays);
TimeSpan spanTimer = dt2 - dt1;
if (spanTimer.TotalMilliseconds < 0) // This could conceivably happen if the have 0 or a negative number (erroneously) for DatabaseBackupFrequencyInDays in configuration.json
{
dt2 = new DateTime(dt1.Year, dt1.Month,
dt1.Day, 1, 0, 0);
dt2 = dt2.AddDays(1);
spanTimer = dt2 - dt1;
}
return spanTimer;
}
public void PerformDatabaseMaintenance()
{
if (BackupCurrentDatabase())
{
var success = CleanupExpiredData();
if (success)
{
Logger.Log(LogLevel.Info, string.Format("Database Maintenance succeeded"));
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupComplete, "Database backup completed");
}
}
}
public void Dispose()
{
_databaseBackupTimer.Elapsed -= DatabaseBackupTimerOnElapsed;
_databaseBackupTimer.Stop();
_databaseBackupTimer.Dispose();
}
private void DatabaseBackupTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
{
try
{
PerformDatabaseMaintenance();
TimeSpan spanTimer = GetDBBackupTimeSpan(_systemConfiguration.DatabaseBackupFrequencyInDays);
_databaseBackupTimer.Interval = Math.Max(spanTimer.TotalMilliseconds, TimeSpan.FromMinutes(10).TotalMilliseconds);
_databaseBackupTimer.Start();
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning,
string.Format("Database Backup Failed: {0}, ",
e.Message));
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed,
"Database backup failed ");
_databaseBackupTimer.Start();
}
}
private bool BackupCurrentDatabase()
{
try
{
_repository.Alerts.Count();
}
catch (Exception ex)
{
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed, "Database backup failed - the database server does not respond or the database does not exist");
throw new InvalidOperationException(string.Format("The DB does not exist : {0} Error {1}", _systemConfiguration.LocalDbPath, ex.Message));
}
if (!Directory.Exists(_systemConfiguration.LocalBackupFolderPath))
Directory.CreateDirectory(_systemConfiguration.LocalBackupFolderPath);
var tries = 0;
var success = false;
while (!success && tries < MaxRetries)
{
try
{
_repository.BackupDatabase(_systemConfiguration.LocalBackupFolderPath);
success = true;
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning, string.Format("Database Backup Failed: {0}, retrying backup", e.Message));
Thread.Sleep(TimeSpan.FromSeconds(1));
tries++;
if (tries == MaxRetries)
{
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed, string.Format("Database backup failed - {0}", e.Message));
}
}
}
var backupDirectory = new DirectoryInfo(_systemConfiguration.LocalBackupFolderPath);
var files = backupDirectory.GetFiles().OrderBy(f => f.CreationTime).ToArray();
if (files.Length > _systemConfiguration.MaxDatabaseBackups)
{
for (var i = 0; i < (files.Length - _systemConfiguration.MaxDatabaseBackups); i++)
{
try
{
files[i].Delete();
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning, string.Format("Failed to delete old backup: {0}", e.Message));
}
}
}
Logger.Log(LogLevel.Info, success ?
"Database Backup succeeded" :
string.Format("Database Backup failed after {0} retries", MaxRetries));
return success;
}
private bool CleanupExpiredData()
{
var success = false;
try
{
var expirationTime = DateTime.Now - TimeSpan.FromDays(_systemConfiguration.DatabaseDataExpirationInDays);
_repository.DeleteTemperatureReadingsBeforeDate(expirationTime);
_repository.DeleteTransactionsBeforeDate(expirationTime);
success = true;
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning, string.Format("Failed to cleanup expired data: {0}", e.Message));
NotifySystemError(ErrorLevel.Log, ErrorCode.DatabaseBackupFailed, string.Format("Database cleanup of expired data failed - {0}", e.Message));
}
Logger.Log(LogLevel.Info, success ?
string.Format("Database clean up expired data succeeded") :
string.Format("Database clean up expired data failed"));
return success;
}
private void NotifySystemError(ErrorLevel errorLevel, ErrorCode errorCode, string description)
{
var handler = SystemError;
if (handler != null)
{
var systemError = _systemErrorFactory.CreateSystemError(errorLevel, errorCode, description);
handler(this, new SystemErrorEventArgs(systemError));
}
}
}
我认为解决方案过于复杂。
- 计时器间隔将触发 Elapsed 事件,而不管前一个 运行 是否已完成,除非您在事件期间明确停止计时器。这应该没有必要。当您输入该方法时,您可以简单地跟踪您是否 运行ning。
将间隔设置为59999。这是一分钟不到的毫秒。然后在事件处理程序的入口处检查当前的小时和分钟是否对应于您要备份的时间。
private bool running = false; private Timer timer = new Timer(); //other code. private void Initialize() { timer.Interval = 59999; myTimer.Elapsed += TimerElapsed; timer.Start(); } public void TimerElapsed(object sender, ElapsedEventArgs e) { if (running) return; DateTime dt = DateTime.Now(); if (!(dt.Hour.Equals(1) && dt.Minute.Equals(0))) return; running = true; //other code running = false; }
此外,我会在配置文件或注册表中保留 运行备份的时间,这样如果我想更改它,我可以在不重新编译我的服务的情况下进行。
我怀疑 Initialize
被多次调用,您可以做的是在创建新实例之前检查 _databaseBackupTimer
是否为 null
如果它不为空,则跳过该方法中的整个代码