我们有一些代码使用System.Timers.Timer来ping数据库连接,并检查是否存在某些已安装的文件。设置计时器的代码是:
m_Timer = new System.Timers.Timer(TimeSpan.FromSeconds(10).TotalMilliseconds);
m_Timer.Elapsed += (object xiSender, ElapsedEventArgs xiEventArgs) =>
{
PingRemoteFolder();
PingDatabase();
};
这发生在单例类的构造函数中。
我知道System.Timers.Timer将使用线程池中的多个线程,如果它每次尝试运行的操作所花费的时间比提供的时间长,但在我们的情况下情况并非如此。
计时器的事件似乎比每10秒更频繁,并且一次最多使用4个或5个线程。
有什么想法吗?此外,我们认为这可能与我们遇到的另一种情况有关,我们在WPF应用程序的主线程上获得堆栈损坏,该应用程序使用工作线程检查使用此计时器设置其值的类的属性。
.NET 4.0版 在Windows 2008R2上
编辑:
当我们在WPF应用程序的主线程上获得堆栈损坏时,会在定时器事件处理程序调用的一个方法中抛出InvalidOperationException,并显示消息“Timeout expired。在获取连接之前已经过了超时时间这可能是因为所有池连接都在使用中并且达到了最大池大小。“
PingRemoteFolder()只是尝试做Directory.GetFiles(“somepath”)
PingDatabase():
private void PingDatabase()
{
if(!string.IsNullOrEmpty(CurrentInstance.Name) && DoPing)
{
SqlConnection sqlConnection = null;
try
{
SqlConnectionStringBuilder sqlConnectionStringBuilder = new SqlConnectionStringBuilder();
sqlConnectionStringBuilder.DataSource = CurrentInstance.Instance;
sqlConnectionStringBuilder.IntegratedSecurity = CurrentInstance.AuthenticationMode == SmoAuthenticationMode.WindowsAuthentication;
sqlConnectionStringBuilder.InitialCatalog = CurrentInstance.Name;
if(CurrentInstance.AuthenticationMode == SmoAuthenticationMode.SqlServerAuthentication)
{
sqlConnectionStringBuilder.UserID = CurrentInstance.UserName;
sqlConnectionStringBuilder.Password = CurrentInstance.Password;
}
sqlConnection = new SqlConnection(sqlConnectionStringBuilder.ToString());
sqlConnection.Open();
Assert.Condition(sqlConnection.State == ConnectionState.Open, "sqlConnection.State == ConnectionState.Open");
IsCurrentInstanceReachable = true;
}
catch(Exception ex)
{
IsCurrentInstanceReachable = false;
CurrentInstance.Name = string.Empty;
}
finally
{
if(sqlConnection != null && (sqlConnection.State != ConnectionState.Closed && sqlConnection.State != ConnectionState.Broken))
{
SqlConnection.ClearPool(sqlConnection);
sqlConnection.Close();
sqlConnection = null;
}
}
}
}
答案 0 :(得分:2)
听起来数据库存在连接池问题,并且调用PingDatabase()时定时器回调阻塞。这会导致多个计时器回调线程同时存在。你可以通过在timer函数的顶部调用m_Timer.Enabled = false并在函数结束时调用m_Timer.Enabled = true来快速检查这一点。这应该导致在任何给定时间只存在一个线程。
m_Timer.Elapsed += (object xiSender, ElapsedEventArgs xiEventArgs) =>
{
m_Timer.Enabled = false;
PingRemoteFolder();
PingDatabase();
m_Timer.Enabled = true;
};