不可重入的计时器

时间:2011-08-14 08:31:05

标签: c# timer thread-safety

我有一个函数,我想调用每个 x 秒,但我希望它是线程安全的。

我在创建计时器时可以设置此行为吗? (我不介意我使用哪个.NET计时器,我只是希望它是线程安全的。)

我知道我可以在回调函数中实现锁定,但我认为如果它在计时器级别会更优雅。

我的回调函数和环境与UI无关。

[编辑1] 我只是不希望我的回调函数中有多个线程。

[编辑2] 我希望将锁定保持在计时器级别内,因为计时器负责何时调用我的回调,这里有一种特殊情况,当我不想调用我的回调函数时。所以我认为何时致电是计时器的责任

5 个答案:

答案 0 :(得分:17)

我猜测,因为你的问题并不完全清楚,你想确保你的计时器在处理回调时无法重新输入你的回调,而你想要在没有锁定的情况下这样做。您可以使用System.Timers.Timer实现此目的,并确保AutoReset属性设置为false。这将确保您必须手动触发每个间隔的计时器,从而防止任何重入:

public class NoLockTimer : IDisposable
{
    private readonly Timer _timer;

    public NoLockTimer()
    {
        _timer = new Timer { AutoReset = false, Interval = 1000 };

        _timer.Elapsed += delegate
        {
            //Do some stuff

            _timer.Start(); // <- Manual restart.
        };

        _timer.Start();
    }

    public void Dispose()
    {
        if (_timer != null)
        {
            _timer.Dispose();
        }
    }
} 

答案 1 :(得分:6)

补充Tim Lloyd针对System.Timers.Timer的解决方案,这里是一个解决方案,可以防止您想要使用System.Threading.Timer的情况进行重入。

TimeSpan DISABLED_TIME_SPAN = TimeSpan.FromMilliseconds(-1);

TimeSpan interval = TimeSpan.FromSeconds(1);
Timer timer = null; // assign null so we can access it inside the lambda

timer = new Timer(callback: state =>
{
  doSomeWork();
  try
  {
    timer.Change(interval, DISABLED_TIME_SPAN);
  }
  catch (ObjectDisposedException timerHasBeenDisposed)
  {
  }
}, state: null, dueTime: interval, period: DISABLED_TIME_SPAN);

我相信你不希望在回调中访问interval,但是这很容易修复,如果你想:将上面的内容放到一个包含了NonReentrantTimer的类中BCL的Timer课程。然后,您将作为参数传递doSomeWork回调。这样一个类的一个例子:

public class NonReentrantTimer : IDisposable
{
    private readonly TimerCallback _callback;
    private readonly TimeSpan _period;
    private readonly Timer _timer;

    public NonReentrantTimer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
    {
        _callback = callback;
        _period = period;
        _timer = new Timer(Callback, state, dueTime, DISABLED_TIME_SPAN);
    }

    private void Callback(object state)
    {
        _callback(state);
        try
        {
            _timer.Change(_period, DISABLED_TIME_SPAN);
        }
        catch (ObjectDisposedException timerHasBeenDisposed)
        {
        }
    }


    public void Dispose()
    {
        _timer.Dispose();
    }
}

答案 2 :(得分:2)

  

我知道我可以在回调函数中实现锁定,但我认为如果它在定时器级别会更优雅

如果需要锁定,那么计时器如何安排呢?你正在寻找一个神奇的免费赠品。

Re Edit1:

您的选择是System.Timers.Timer和System.Threading.Timer,两者都需要防止重新进入。请参阅this page并查找处理计时器事件重入部分。

答案 3 :(得分:0)

private void getbarCode()
        {
            byte[] data = new byte[1024];
            int receivedDataLength = lidhje.Receive(data);
            numberCodeBar = Encoding.ASCII.GetString(data, 0, receivedDataLength);
        }

答案 4 :(得分:-1)

计时器如何知道您的共享数据?

在某些ThreadPool线程上执行定时器回调。所以你将至少拥有2个主题:

  1. 创建和启动计时器的主线程;
  2. 来自ThreadPool的线程,用于启动回调。
  3. 您有责任正确处理共享数据。

    重新编辑: chibacity提供了一个完美的例子。