我是否可以使用C#计时器正确实现此缓冲区?

时间:2019-02-05 21:46:24

标签: c# timer thread-safety

我有一项服务,用于订阅对存储库的更新。 收到更新消息后,该服务需要从存储库中重新加载一些数据。

但是,可以在短时间内接收到许多更新消息。因此,我想创建一个缓冲区/时间窗口,这意味着在到达许多更新消息的那段时间内,只会发生一次重新加载。

我创建了一个非常粗糙的轮廓:

class TestService
{
    private Timer scheduledReloadTimer;

    public void AttemptReload()
    {
        if (scheduledReloadTimer == null)
        {
            Console.WriteLine("Scheduling reload...");

            scheduledReloadTimer = new Timer(Reload, null, 10000, Timeout.Infinite);
        }
        else
        {
            Console.WriteLine("Reload already scheduled for this period...");
        }
    }

    private void Reload(object stateInfo)
    {
        scheduledReloadTimer.Dispose();
        scheduledReloadTimer = null;

        Console.WriteLine("Doing reload..");
    }
}

在Timer上使用空检查是否足够好,以查看是否已安排重新加载?

我正确放置了计时器吗?

这里还有其他我想念的东西吗?

我看到了另一个stackoverflow答案,建议使用Reactive Extensions实现此目的:https://stackoverflow.com/a/42887221/67357,但这是矫over过正吗?

1 个答案:

答案 0 :(得分:1)

您在这里确实存在潜在的线程安全问题。一种快速的解决方案是在代码的关键部分周围创建线程锁定范围,以确保在检查/创建和设置计时器变量时,没有其他线程可以进入并并行启动同一进程:

class TestService
{
    private Timer scheduledReloadTimer;
    private object timerLock = new object();

    public void AttemptReload()
    {
        lock (timerLock)
        {
            if (scheduledReloadTimer == null)
            {
                Console.WriteLine("Scheduling reload...");

                scheduledReloadTimer = new Timer(Reload, null, 10000, Timeout.Infinite);
            }
            else
            {
                Console.WriteLine("Reload already scheduled for this period...");
            } 
        }
    }

    private void Reload(object stateInfo)
    {
        lock (timerLock)
        {
            scheduledReloadTimer.Dispose();
            scheduledReloadTimer = null; 
        }

        Console.WriteLine("Doing reload..");
    }
}

响应式扩展 是解决此节流问题的好方法-因为已经为您编写了代码。

另一种方法可能是修改AttemptReload调用,以简单地重置计时器上的间隔(如果reloadTimer!= null),实质上是在以后每次对AttemptReload的调用中都推迟计时器事件的调用。

这样,在最后一次调用AttemptReload + 10,000毫秒之后,计时器肯定不会启动。