C# 非重入定时器

C# 非重入定时器,c#,timer,thread-safety,C#,Timer,Thread Safety,我有一个函数,我想每x秒调用一次,但我希望它是线程安全的 我可以在创建计时器时设置此行为吗?(我不介意使用哪个.NET计时器,我只希望它是线程安全的) 我知道我可以在回调函数中实现锁,但我认为如果是在计时器级别,它会更优雅 我的回调函数和环境与UI无关 [编辑1] 我只是不希望回调函数中有多个线程 [编辑2] 我希望将锁定保持在计时器级别内,因为计时器负责何时调用回调函数,这里有一种特殊情况,即我不想调用回调函数。所以我认为什么时候打电话是计时器的责任 我知道我可以在回调函数中实现锁,但我认为如

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

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

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

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

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

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

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

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

重新编辑1:


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

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

计时器回调在某些线程池线程上执行。因此,您将至少有2个线程:

  • 创建并启动计时器的主线程
  • 线程池中用于启动回调的线程
  • 您有责任为共享数据提供正确的工作


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

    我猜,由于您的问题并不完全清楚,您希望确保计时器在处理回调时不能重新进入回调,并且您希望在不锁定的情况下执行此操作。您可以使用
    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();
            }
        }
    } 
    
    public类NoLockTimer:IDisposable
    {
    专用只读定时器_定时器;
    公共NoLockTimer()
    {
    _计时器=新计时器{AutoReset=false,间隔=1000};
    _timer.appead+=委托
    {
    //做点什么
    
    _timer.Start();//补充了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
    ,但是如果您想:将上述内容放入一个
    nonerenttimer
    类中,该类包装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();
        }
    }
    
    使用系统;
    使用系统诊断;
    /// 
    ///更新了代码。
    /// 
    公共类NicerFormTimer:IDisposable{
    公共空间处置(){
    使用(this.Timer){}
    总干事(本);
    }
    private System.Windows.Forms.Timer{get;}
    /// 
    ///在给定的间隔(in)后执行。
    /// 
    /// 
    ///再次执行。(重新启动。)
    /// 
    public NicerFormTimer(动作动作,布尔重复,Int32?毫秒=null){
    if(action==null){
    返回;
    }
    this.Timer=new System.Windows.Forms.Timer{
    间隔=毫秒。GetValuerDefault(1000)
    };
    this.Timer.Tick+=(发送方,参数)=>{
    试一试{
    this.Timer.Stop();
    动作();
    }
    捕获(异常){
    Debug.WriteLine(异常);
    }
    最后{
    如果(重复){
    this.Timer.Start();
    }
    }
    };
    this.Timer.Start();
    }
    }
    /// 
    ///更新了代码。
    /// 
    公共类尼斯系统计时器:IDisposable{
    公共空间处置(){
    使用(this.Timer){}
    总干事(本);
    }
    private System.Timers.Timer{get;}
    /// 
    ///在给定的间隔(in)后执行。
    /// 
    /// 
    ///再次执行。(重新启动。)
    /// 
    public NicerSystemTimer(动作动作,布尔重复,双毫秒=null){
    if(action==null){
    返回;
    }
    this.Timer=new System.Timers.Timer{
    自动重置=错误,
    间隔=毫秒。GetValuerDefault(1000)
    };
    this.Timer.appeased+=(发送方,参数)=>{
    试一试{
    this.Timer.Stop();
    动作();
    }
    捕获(异常){
    Debug.WriteLine(异常);
    }
    最后{
    如果(重复){
    this.Timer.Start();
    }
    }
    };
    this.Timer.Start();
    }
    }
    
    您没有提供足够的信息…您的函数是否访问程序的任何其他部分?如果没有,则它已经是线程安全的(有计时器和没有计时器)…如果是,则计时器与threadsafety无关,但您需要使用一些锁等。为什么要将锁完全排除在回调函数之外?编辑2没有意义:计时器负责按时调用您的函数,您的函数负责正确处理共享数据(lokcing等)-时间不能对你的数据负责,你的功能是…计时器本身是线程安全的-你的代码可能不是,但给出的信息不足以回答如何使其线程安全。请参阅我的编辑1,它对你有意义吗?@Hank I read
    using System;
    using System.Diagnostics;
    
    /// <summary>
    ///     Updated the code.
    /// </summary>
    public class NicerFormTimer : IDisposable {
    
        public void Dispose() {
            using ( this.Timer ) { }
    
            GC.SuppressFinalize( this );
        }
    
        private System.Windows.Forms.Timer Timer { get; }
    
        /// <summary>
        ///     Perform an <paramref name="action" /> after the given interval (in <paramref name="milliseconds" />).
        /// </summary>
        /// <param name="action"></param>
        /// <param name="repeat">Perform the <paramref name="action" /> again. (Restarts the <see cref="Timer" />.)</param>
        /// <param name="milliseconds"></param>
        public NicerFormTimer( Action action, Boolean repeat, Int32? milliseconds = null ) {
            if ( action == null ) {
                return;
            }
    
            this.Timer = new System.Windows.Forms.Timer {
                Interval = milliseconds.GetValueOrDefault( 1000 )
            };
    
            this.Timer.Tick += ( sender, args ) => {
                try {
                    this.Timer.Stop();
                    action();
                }
                catch ( Exception exception ) {
                    Debug.WriteLine( exception );
                }
                finally {
                    if ( repeat ) {
                        this.Timer.Start();
                    }
                }
            };
    
            this.Timer.Start();
        }
    
    }
    
    /// <summary>
    ///     Updated the code.
    /// </summary>
    public class NicerSystemTimer : IDisposable {
    
        public void Dispose() {
            using ( this.Timer ) { }
    
            GC.SuppressFinalize( this );
        }
    
        private System.Timers.Timer Timer { get; }
    
        /// <summary>
        ///     Perform an <paramref name="action" /> after the given interval (in <paramref name="milliseconds" />).
        /// </summary>
        /// <param name="action"></param>
        /// <param name="repeat">Perform the <paramref name="action" /> again. (Restarts the <see cref="Timer" />.)</param>
        /// <param name="milliseconds"></param>
        public NicerSystemTimer( Action action, Boolean repeat, Double? milliseconds = null ) {
            if ( action == null ) {
                return;
            }
    
            this.Timer = new System.Timers.Timer {
                AutoReset = false,
                Interval = milliseconds.GetValueOrDefault( 1000 )
            };
    
            this.Timer.Elapsed += ( sender, args ) => {
                try {
                    this.Timer.Stop();
                    action();
                }
                catch ( Exception exception ) {
                    Debug.WriteLine( exception );
                }
                finally {
                    if ( repeat ) {
                        this.Timer.Start();
                    }
                }
            };
    
            this.Timer.Start();
        }
    
    }