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秒触发。