【问题标题】:Infinite while loop in Windows ServiceWindows 服务中的无限循环
【发布时间】:2014-10-24 04:40:21
【问题描述】:

我有一个窗口,我在 OnStart() 方法中添加了一个无限循环。我已经测试了该服务 1 小时,它运行良好。但因为这是我的第一个 Windows Service 所以有疑问无限循环的性能。 这是代码..

 protected override void OnStart(string[] args)
    {
       while(true){

        string Query="";

        Query = "SELECT * FROM 'reportsetting` order by SendingTime;";

        MySqlConnection con = new MySqlConnection(conn);
        MySqlCommand comm = new MySqlCommand(Query, con);
        con.Open();
        MySqlDataReader dr = comm.ExecuteReader();
        while (dr.Read())
        {

            time = dr["SendingTime"].ToString();

            if ((str = DateTime.Now.ToString("HH:mm")).Equals(time))
            {

                //Execute Function and send reports based on the data from the database.

                Thread thread = new Thread(sendReports);
                thread.Start();

            }


        }


            //Halt for this Moment

            while ((str = DateTime.Now.ToString("HH:mm")).Equals(time))
            {


            }


         }

        }

    public void sendReports() { 



    }

所以想知道从长远来看是否可以。谢谢..

【问题讨论】:

  • 这听起来像是一个非常糟糕的主意,但您没有描述足够的细节。循环中是否有任何中断条件?
  • @JamesCross 根据条件,我必须不断地从数据库中读取数据,然后根据条件在线程中做一些工作
  • @JamesCross 我已经用完整的代码更新了我的帖子。请看看
  • 该代码效率极低。但是要解决您的 while(true) 问题,请查看使用计时器。当您再次需要查询时,让系统每分钟左右给您回电:stackoverflow.com/questions/10206000/…
  • @JamesCross 实际上,根据我的情况,我必须查询每个 40 seconds 并在其后执行代码..

标签: c# windows-services infinite-loop


【解决方案1】:

每 40 秒重新运行一次查询:

private const string Query = "SELECT * FROM 'reportsetting` order by SendingTime;"

protected override void OnStart(string[] args)
{
    _timer = new Timer(40 * 1000); // every 40 seconds
    _timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
    _timer.Start(); // <- important
}

private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    MySqlConnection con = new MySqlConnection(conn);
    MySqlCommand comm = new MySqlCommand(Query, con);
    con.Open();
    MySqlDataReader dr = comm.ExecuteReader();
    while (dr.Read())
    {

        time = dr["SendingTime"].ToString();

        if ((str = DateTime.Now.ToString("HH:mm")).Equals(time))
        {

            //Execute Function and send reports based on the data from the database.

            Thread thread = new Thread(sendReports);
            thread.Start();
        }
    }
}

类似的东西。 正如 Groo 所提到的,您可能希望每次都处理连接,这样您就不会在内存中徘徊。

【讨论】:

  • 是的,只是显示了 40 秒的位置。无论如何,编译器都会为我们解决所有问题。那里没有效率低下。
  • @JamesCross 我需要给1 minute多少时间。我的意思是我必须为60*1000添加什么价值。60*1000
  • 没错。 10 分钟再去一次10 * 60 * 1000
  • @JamesCross 好的,我还有一个疑问,因为我的while (dr.Read()) 如果满足if ((str = DateTime.Now.ToString("HH:mm")).Equals(time)) 条件,那么Thread thread = new Thread(sendReports); 将在后台执行,下一个time 值我们将获得或首先线程将执行然后它将进入 while(dr) 循环
  • 是的,sendReports 方法将在新线程中运行,但 while(dr.Read()) 仍将在与以前相同的线程中运行。
【解决方案2】:

您几乎肯定不想在无限循环中查询数据库。您可能想要做的是使用Timer 每隔一段时间(例如每 30 秒)查询一次数据库,并在某些条件匹配时做一些工作,例如

private static Timer timer;
private const int TimerInterval = 30000;

protected override void OnStart(string[] args)
{
    var callback = new TimerCallback(checkDatabase);
    this.timer = new Timer(callback, null, 0, TimerInterval);
}

private void checkDatabase()
{
    string query = "SELECT * FROM 'reportsetting` order by SendingTime;";
    using (var con = new MySqlConnection(conn))
    using (var cmd = new MySqlCommand(query, con))
    {
        con.Open();
        using (var dr = cmd.ExecuteReader())
        {
            while (dr.Read())
            {
                // do some work
            }
        }
    }
}

根据更新速度的重要性,您还可以聪明地处理事情并引入退避策略。例如,这是一个非常简单的增量退避:

private const int DefaultInterval = 40000;
private int interval = DefaultInterval;
...

while (dr.Read())
{
    if (someCondition)
    {
        // do some work
        timer.Change(DefaultInterval, DefaultInterval); // revert to 40 seconds 
    }
    else 
    {
        // no change? increase polling by 10 seconds
        interval += 10000;
        timer.Change(interval, interval);
    }
}

【讨论】:

  • 退避是增量的,而不是指数的。
  • @ssmith 哈,确实如此 - 这有点无关紧要,只是想举个例子(尽管会更新以确保正确性)。谢谢
【解决方案3】:

或者你可以在新线程中启动它而不使用计时器

var threadStart = new ThreadStart(<your method delegate>);
var thread = new Thread(threadStart);
thread.Start();

【讨论】:

    【解决方案4】:

    试试这个

    bool _IsStop = false;
    
    protected override void OnStart(string[] args)
    {
            base.OnStart(args);
    
            while (!this._IsStop)
            {
    
                this.Process();
    
                //40 seconds time out
                Thread.Sleep(1000*40); //1000 is 1 second
            }
    
            //If we reach here
            //Do some clean up here
    
        }
    
    
        protected override void OnStop()
        {
            //Service is stop by the user on service or by OS
            this._IsStop = true;
    
        }
    
    
        private void Process()
        {
            string Query = "";
    
            // -->>>  if you use something like this on your query and little bit setup on your db
            // this will give you a more efficient memory usage
            Query = "SELECT * FROM reportsetting where [your tag if already sent] = false and SendingTime <= [current date and time]  order by SendingTime;";
    
            MySqlConnection con = new MySqlConnection(conn);
            MySqlCommand comm = new MySqlCommand(Query, con);
            con.Open();
            MySqlDataReader dr = comm.ExecuteReader();
            while (dr.Read())
            {
                    //Execute Function and send reports based on the data from the database.
    
                    Thread thread = new Thread(sendReports);
                    thread.Start();
    
                    //sleep for half a second before proceeding to the next record if any
                    Thread.Sleep(500); 
            }
        }
    

    【讨论】:

    • 我是否会从数据库中获取新的时间值,而 sendReports() 将在后台运行..我说得对吗?
    • 一旦执行查询循环,您可以将日期作为您的标准传递,该标准是从 c# 或数据库中计算出来的(抱歉,我不熟悉 MySQL dateAdd 函数)。跨度>
    • 是的,因为您为 sendReports 的每条记录生成线程
    • 对不起,但我没有得到你。我怀疑我的函数 sendReport() 是否会在后台作为线程执行(或者我不熟悉这些对不起),我会移动到While(dr.Read()) 并将获得新的time 值,随后将启动新线程
    • 本案例将在后台运行,因为您每次记录迭代都会创建一个线程,这意味着它们将独立工作,直到该过程完成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 2013-02-02
    • 2012-03-20
    • 1970-01-01
    • 1970-01-01
    • 2019-12-23
    相关资源
    最近更新 更多