使用.NET 4.5定期执行事件

时间:2014-04-03 13:36:11

标签: .net multithreading timer

我在.NET 4.5中构建一个小型stat客户端库,需要收集队列中的数据,然后通过发送一些HTTP消息定期处理队列中的数据。我正在寻找关于实现此代码周期部分的最佳方法的一些指导。

我认为使用ConcurrentQueue类最有意义来保存数据,客户端类的简化版本如下所示:

public class StatClient
{
    private static readonly ConcurrentQueue<Stat> Stats = new ConcurrentQueue<Stat>();

    public void RecordCount(string name, long count)
    {
        Stats.Enqueue(new CounterStat(name, count));
    }
}

那么从队列中读取的每10秒触发后台进程的首选方法是什么?我应该只使用System.Timers.Timer然后启动一个新的线程或任务,或者在启动时创建一个后台线程并使其休眠10秒,或者???

2 个答案:

答案 0 :(得分:2)

我建议你使用BlockingCollection<stat>而不是ConcurrentQueue<Stat>。而不是有一个为队列提供服务的定期任务,只需要一个不断查看队列的消费者线程。例如:

BlockingCollection<Stat> _stats = new BlockingCollection<Stat>();

public void RecordCount(string name, long count)
{
    Stats.Add(new CounterStat(name, count));
}

您的消费者看起来像这样:

public void Consumer()
{
    foreach (var stat in _stats.GetConsumingEnumerable())
    {
        // process stat
    }
}

您可以将消费者作为线程或任务启动。作为一项任务,例如:

Task consumer = Task.Factory.StartNew(Consumer, TaskCreationOptions.LongRunning);

消费者将阻止,等待将某些内容添加到队列中。一旦添加了某些内容,消费者就会出列并处理它。

要停止消费者,请致电_stats.CompleteAdding()。这将表明不再有任何项目添加到队列中。消费者将清空队列,然后退出循环。

如果你真的想要使用周期性任务,那么我建议使用计时器。但是让计时器一次性完成,这样你就不能进行并发调用。每次打勾后你都会重置计时器。像这样:

// create the timer
System.Timers.Timer timer = new Timer();
timer.Elapsed += TimerTick;
timer.Interval = 10000;
timer.AutoReset = false;  // makes a one-shot timer rather than a periodic timer
timer.Enabled = true;

void TimerTick(object sender, EventArgs e)
{
    // do something with the queue
    // then reset the timer
    timer.Enabled = true;
}

无论您采用哪种方式,我强烈建议您使用BlockingCollection代替ConcurrentQueueBlockingCollection的API更容易使用,包括非繁忙等待,GetConsumingEnumerable等好东西。更强大。

答案 1 :(得分:0)

我认为如你所说,有一个后台进程可以正常工作。在我看来,这并不像定时器引发事件和每次启动单独的后台进程那么复杂,因为如果先前生成的进程与新进程重叠并且正在争取访问资源,那么你可能遇到并发问题 - 你的{ {1}}。当然,您可以始终使用互斥锁来协调对该资源的访问,但另一方面则不那么复杂。