1

我们有一些代码使用 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 应用程序的主线程上遇到堆栈损坏,该应用程序使用工作线程检查使用此计时器设置其值的类的属性。

Windows 2008R2 上的 .NET 4.0 版

编辑:

此外,当我们在 WPF 应用程序的主线程上发现堆栈损坏时,在计时器事件处理程序调用的方法之一中引发 InvalidOperationException,并显示消息“超时已过期。在从池中获取连接之前已过超时时间。这可能是因为所有池连接都在使用中并且达到了最大池大小。”

PingRemoteFolder() 只是尝试做 Directory.GetFiles("somepath")

平数据库():

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;
            }
        }
    }
}
4

1 回答 1

2

听起来数据库存在连接池问题,并且计时器回调阻塞了对 PingDatabase() 的调用。这会导致多个定时器回调线程同时存在。您可以通过在计时器函数顶部调用 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;
};
于 2013-04-26T20:58:15.277 回答