C# 每秒触发一个事件

C# 每秒触发一个事件,c#,.net,wpf,multithreading,C#,.net,Wpf,Multithreading,我有一个应用程序,需要检查网站饲料每一秒 有时对服务器的请求长于一秒钟。在这种情况下,应用程序需要等待第一个请求完成,然后立即启动一个新请求。我如何实现这一点 此外,请求不应冻结GUI。计时器对象可能会帮您实现这一点。你可以设置它每秒钟点火一次。定时器触发时执行的代码将首先禁用定时器,执行其工作,然后重新启用定时器。通过禁用计时器,它将不会再次启动,直到它完成处理。我倾向于使用这样一个单独的线程: var thread = new Thread(() => { while(!Stop)

我有一个应用程序,需要检查网站饲料每一秒

有时对服务器的请求长于一秒钟。在这种情况下,应用程序需要等待第一个请求完成,然后立即启动一个新请求。我如何实现这一点


此外,请求不应冻结GUI。

计时器对象可能会帮您实现这一点。你可以设置它每秒钟点火一次。定时器触发时执行的代码将首先禁用定时器,执行其工作,然后重新启用定时器。通过禁用计时器,它将不会再次启动,直到它完成处理。

我倾向于使用这样一个单独的线程:

var thread = new Thread(() =>
{
  while(!Stop)
  {
    var nextCheck = DateTime.Now.AddSeconds(1);

    CheckWebSite();
    Application.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
    {
      UpdateViewModelWithNewData();
    }));
    int millis = (int)(nextCheck - DateTime.Now).TotalMilliseconds();
    if(millis>0)
      Thread.Sleep(millis);
  }
});
thread.IsBackground = true;
thread.Start();
    System.Timers.Timer timer = new System.Timers.Timer(1000);

    public void StartTimer()
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
        timer.Start();
    }

    private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
    {
        DateTime start;
        TimeSpan elapsed = TimeSpan.MaxValue;

        timer.Stop();

        while (elapsed.TotalSeconds > 1.0)
        {
            start = DateTime.Now;

            // check your website here

            elapsed = DateTime.Now - start;
        }
        timer.Interval = 1000 - elapsed.TotalMilliseconds;
        timer.Start();
    }
Func<Response> checkWebsiteFeed = ...;
System.Timers.Timer timer = new System.Timers.Timer(1000);

public void StartTimer()
{
    timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
    timer.Start();
}

private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
{
    DateTime start;
    TimeSpan elapsed = TimeSpan.MaxValue;

    timer.Stop();

    while (elapsed.TotalSeconds > 1.0)
    {
        start = DateTime.Now;

        // check your website here

        elapsed = DateTime.Now - start;
    }
    timer.Interval = 1000 - elapsed.TotalMilliseconds;
    timer.Start();
}
Dispatcher.Invoke
转换到实际UI更新的UI线程。这将非常有效地实现生产者-消费者


thread.IsBackground=true
会在应用程序结束时导致线程停止。如果您想让它更快停止,请将“停止”值设置为“true”。上面代码中的“Stop”值被假定为类的bool属性,但它可以是任何东西,甚至是局部变量。

我将使用生产者-消费者模式的简单变体。您将有两个thead,一个生产者和一个消费者,它们共享一个整数变量。生产者将拥有一个每秒触发一次的
系统.Threading.Timer
,此时它将
联锁。递增变量并调用消费者。耗电元件逻辑反复检查馈电和
联锁。当计数器大于零时,减量
计数器。消费者逻辑将受到一个
监视器.TryEnter
的保护,该监视器将处理重新进入。下面是示例代码

public static FeedCheck{
  int _count = 0;
  static object _consumingSync = new object();
  static Threading.Timer _produceTimer;

  private static void Consume() {
    if (!Monitor.TryEnter(_consumingSync)) return;

    try {
      while(_count > 0) {
        // check feed
        Interlocked.Decrement(ref _count);
      }
    }
    finally { Monitor.Exit(_consumingSync); }
  }

  private static void Produce() {
    Interlocked.Increment(ref _count);
    Consume();
  }

  public static void Start() {
    // small risk of race condition here, but not necessarily
    // be bad if multiple Timers existed for a moment, since only
    // the last one will survive.
    if (_produceTimer == null) {
      _produceTimer = new Threading.Timer(
        _ => FeedCheck.Produce(), null, 0, 1000
      );
    }
  }
}
用法:

FeedCheck.Start();
关于.NET线程的一个很好的资源(除了MSDN库的东西)是Jon Skeet的文档,其中包括“更多
Monitor
methods”下的内容

顺便说一句,真正的生产者-消费者模式围绕着工作数据的集合,一个或多个线程通过向该集合添加数据来产生工作,而一个或多个其他线程通过从该集合中删除数据来工作。在上面的变体中,“工作数据”仅仅是我们需要立即检查提要的次数的计数


(另一种方法,不是让计时器回调调用消耗,而是让计时器回调锁定并脉冲消耗等待的
监视器。在这种情况下,消耗有一个无限循环,如
while(true)
,您可以在它自己的线程中启动一次。因此,不需要通过调用
Monitor.TryEnter
)来支持重新进入。

您可以尝试创建一个主应用程序,其中包含您不希望冻结的GUI。它将用一个循环启动一个线程,该循环将在一段特定的时间后迭代。如果计时器等于/超过设置的时间,则为请求启动另一个线程。但是,在启动之前检查线程是否已经存在。如果没有,则阻止执行,直到请求线程完成。 e、 g:

注: 不要分秒必争。正如其他海报所说,这有点不尊重网站所有者。您有可能被网站所有者拒绝访问提要。

使用如下计时器:

var thread = new Thread(() =>
{
  while(!Stop)
  {
    var nextCheck = DateTime.Now.AddSeconds(1);

    CheckWebSite();
    Application.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
    {
      UpdateViewModelWithNewData();
    }));
    int millis = (int)(nextCheck - DateTime.Now).TotalMilliseconds();
    if(millis>0)
      Thread.Sleep(millis);
  }
});
thread.IsBackground = true;
thread.Start();
    System.Timers.Timer timer = new System.Timers.Timer(1000);

    public void StartTimer()
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
        timer.Start();
    }

    private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
    {
        DateTime start;
        TimeSpan elapsed = TimeSpan.MaxValue;

        timer.Stop();

        while (elapsed.TotalSeconds > 1.0)
        {
            start = DateTime.Now;

            // check your website here

            elapsed = DateTime.Now - start;
        }
        timer.Interval = 1000 - elapsed.TotalMilliseconds;
        timer.Start();
    }
Func<Response> checkWebsiteFeed = ...;
System.Timers.Timer timer = new System.Timers.Timer(1000);

public void StartTimer()
{
    timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
    timer.Start();
}

private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
{
    DateTime start;
    TimeSpan elapsed = TimeSpan.MaxValue;

    timer.Stop();

    while (elapsed.TotalSeconds > 1.0)
    {
        start = DateTime.Now;

        // check your website here

        elapsed = DateTime.Now - start;
    }
    timer.Interval = 1000 - elapsed.TotalMilliseconds;
    timer.Start();
}

已用事件是在线程池线程上处理的,因此如果需要从已用处理程序更新UI,则必须记住这一点。

我认为现在处理这一问题的最简单方法是使用Microsoft反应式框架(Rx)

因此,假设我有一个函数调用网站提要并返回一个
响应
对象,如下所示:

var thread = new Thread(() =>
{
  while(!Stop)
  {
    var nextCheck = DateTime.Now.AddSeconds(1);

    CheckWebSite();
    Application.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
    {
      UpdateViewModelWithNewData();
    }));
    int millis = (int)(nextCheck - DateTime.Now).TotalMilliseconds();
    if(millis>0)
      Thread.Sleep(millis);
  }
});
thread.IsBackground = true;
thread.Start();
    System.Timers.Timer timer = new System.Timers.Timer(1000);

    public void StartTimer()
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
        timer.Start();
    }

    private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
    {
        DateTime start;
        TimeSpan elapsed = TimeSpan.MaxValue;

        timer.Stop();

        while (elapsed.TotalSeconds > 1.0)
        {
            start = DateTime.Now;

            // check your website here

            elapsed = DateTime.Now - start;
        }
        timer.Interval = 1000 - elapsed.TotalMilliseconds;
        timer.Start();
    }
Func<Response> checkWebsiteFeed = ...;
System.Timers.Timer timer = new System.Timers.Timer(1000);

public void StartTimer()
{
    timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
    timer.Start();
}

private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
{
    DateTime start;
    TimeSpan elapsed = TimeSpan.MaxValue;

    timer.Stop();

    while (elapsed.TotalSeconds > 1.0)
    {
        start = DateTime.Now;

        // check your website here

        elapsed = DateTime.Now - start;
    }
    timer.Interval = 1000 - elapsed.TotalMilliseconds;
    timer.Start();
}
在后台线程上调用
checkWebsiteFeed
,在UI线程上运行
Subscribe
。超级简单

使用如下计时器:

var thread = new Thread(() =>
{
  while(!Stop)
  {
    var nextCheck = DateTime.Now.AddSeconds(1);

    CheckWebSite();
    Application.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
    {
      UpdateViewModelWithNewData();
    }));
    int millis = (int)(nextCheck - DateTime.Now).TotalMilliseconds();
    if(millis>0)
      Thread.Sleep(millis);
  }
});
thread.IsBackground = true;
thread.Start();
    System.Timers.Timer timer = new System.Timers.Timer(1000);

    public void StartTimer()
    {
        timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
        timer.Start();
    }

    private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
    {
        DateTime start;
        TimeSpan elapsed = TimeSpan.MaxValue;

        timer.Stop();

        while (elapsed.TotalSeconds > 1.0)
        {
            start = DateTime.Now;

            // check your website here

            elapsed = DateTime.Now - start;
        }
        timer.Interval = 1000 - elapsed.TotalMilliseconds;
        timer.Start();
    }
Func<Response> checkWebsiteFeed = ...;
System.Timers.Timer timer = new System.Timers.Timer(1000);

public void StartTimer()
{
    timer.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerHandler);
    timer.Start();
}

private void TimerHandler(object sender, System.Timers.ElapsedEventArgs e)
{
    DateTime start;
    TimeSpan elapsed = TimeSpan.MaxValue;

    timer.Stop();

    while (elapsed.TotalSeconds > 1.0)
    {
        start = DateTime.Now;

        // check your website here

        elapsed = DateTime.Now - start;
    }
    timer.Interval = 1000 - elapsed.TotalMilliseconds;
    timer.Start();
}

只是一个想法,但如果这是一个公共网站,你的建议是非常不赞成。每秒钟访问一个网站都会带来很多额外的流量。为了让你的GUI不冻结,你肯定需要研究线程。也许一个后台工作人员会这样做。事实上,我正在请求网站API。他们允许对API的请求不超过每秒一次。如果对网站的请求持续超过一秒,请求完成后,应用程序将在发送新请求之前等待一秒钟,但我需要它立即发送新请求,我认为它会工作。我还有一个问题,如果我要检查多个站点(例如10)。我想同时从他们那里获取信息。我需要为每个站点启动新线程吗?这是一个好的idia吗?如果只有10个站点,我会坚持使用这个解决方案,只启动10个线程。如果有100个站点,我会使用
WebRequest.BeginGetResponse
IAsyncResult
。但是请确保您的传入管道足够大,这样您就不会开始丢弃数据包。顺便说一下,欢迎使用StackOverflow。别忘了给你觉得有用的答案投票。谢谢。你的回答很有用。我只有15岁才能投票:(如果请求超过1秒,这不是要在请求完成后等待1秒,而不是立即触发吗?@CccTrash:Oops-我误读了你的问题。我编辑了TimerHandler以反复检查,直到经过的时间降到1秒以下,然后调整计时器,使其在最后一个计时器触发后1秒触发。