【问题标题】:Is there any standard Poller example class in c#c# 中是否有任何标准的 Poller 示例类
【发布时间】:2011-08-24 09:40:10
【问题描述】:

有没有 poller 类的好例子?线程安全的等待句柄、后台/前台选项以及常见的启动、停止、暂停、恢复调用?

我找到了像 CruiseControl poller 这样的例子,但理想情况下,我希望避免每次我想轮询某事时都实施 IPollable。

谢谢

【问题讨论】:

    标签: c# multithreading polling


    【解决方案1】:

    除了基本的 .NET 计时器类之外,我不知道其他标准,但这里有一个 .NET 计时器的包装器类,它使用委托而不是您找到的包装器的 IPollable 接口。它还具有一些锁定功能和一些派上用场的方法。例如,您可能希望自己扩展和改进它以用于暂停/恢复和日志记录。祝你好运。

    public class TimerWrapper
    {
        private object defaultLock = new object();
        private object functionLock = null;
        private object classLock = new object();
        protected bool isRunning = false;
        protected bool isRepeating = false;
        protected Timer timer = null;
        protected Action timerFn = null;
    
        public TimerWrapper(Action timerFn)
        {
            if (timerFn == null)
            {
                throw new ArgumentNullException("timerFn", "Invalid timer delegate supplied at construction");
            }
            // Execute this function upon expiration of the timer
            this.timerFn = timerFn;
        }
    
        public TimerWrapper(Action timerFn, ref object timerLock) : this(timerFn)
        {
            // Use the locking object passed at construction
            this.functionLock = timerLock;
        }
    
        protected void TimerFunction(object state)
        {
            if (timerFn != null)
            {
                lock (classLock)
                {
                    // Lock on function lock if available or default lock otherwise
                    lock (functionLock ?? defaultLock)
                    {
                        // If timer isn't repeating it's now no longer running
                        if (!IsRepeating)
                        {
                            IsRunning = false;
                        }
    
                        // Execute this function because timer has expired
                        timerFn();
                    }
                }
            }
        }
    
        public void Stop()
        {
            lock (classLock)
            {
                if (timer != null)
                {
                    timer.Dispose();
                    timer = null;
                }
    
                IsRunning = false;
            }
        }
    
        public void Start(int duetime)
        {
            // Start the timer for a single run
            Start(duetime, Timeout.Infinite);
        }
    
        public void Start(int duetime, int period)
        {
            if (duetime > 0)
            {
                lock (classLock)
                {
                    // Stop the timer
                    Stop();
    
                    // Start the timer for either a single run or repeated runs
                    timer = new Timer(TimerFunction, null, duetime, period);
    
                    IsRunning = true;
                    IsRepeating = (period != Timeout.Infinite);
                }
            }
        }
    
        public bool IsRepeating
        {
            get
            {
                return isRepeating;
            }
            protected set
            {
                if (isRepeating != value)
                {
                    isRepeating = value;
                }
            }
        }
    
        public bool IsRunning
        {
            get
            {
                return isRunning;
            }
            protected set
            {
                if (isRunning != value)
                {
                    isRunning = value;
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      感谢 mtijn,查看了您的示例并编写了我自己的 poller,它完成了 poller 应该做的所有事情。任何 cmets 都表示赞赏。

      public class Poller
      {
          private static readonly ILog _log = LogManager.GetLogger(typeof(Poller));
      
          private readonly Action _action;
          private readonly int _pollingInterval;
          private readonly Thread _processingThread;
          private readonly AutoResetEvent _stopEvent;
          private readonly ManualResetEventSlim _pauseEvent;
          private readonly object _syncLock = new object();
          private PollerState _pollerState;
      
          public Poller(string pollerName, Action action, int pollingInterval, bool isBackground)
          {
              _action = action;
              _pollingInterval = pollingInterval;
      
              _stopEvent = new AutoResetEvent(false);
              _pauseEvent = new ManualResetEventSlim(false);
              _processingThread = new Thread(DoWork) { IsBackground = isBackground, Name = pollerName };
      
              _pollerState = PollerState.Unstarted;
          }
      
          public void Start()
          {
              _pollerState = PollerState.Running;
              _processingThread.Start();
          }
      
          public void Start(int dueTime)
          {
              new Timer(o => Start(), null, dueTime, Timeout.Infinite);
          }
      
          public void Stop()
          {
              lock (_syncLock)
              {
                  if (_pollerState != PollerState.Running && _pollerState != PollerState.PauseRequested)
                      _log.WarnFormat("Requested STOP on {0} poller state.", _pollerState);
      
                  _pollerState = PollerState.StopRequested;
                  _stopEvent.Set();
                  _pauseEvent.Set();
              }
          }
      
          public void Pause()
          {
              lock (_syncLock)
              {
                  if (_pollerState != PollerState.Running)
                      _log.WarnFormat("Requested PAUSE on {0} poller state.", _pollerState);
      
                  _pauseEvent.Reset();
                  _pollerState = PollerState.PauseRequested;
              }
          }
      
          public void Continue()
          {
              lock(_syncLock)
              {
                  if (_pollerState == PollerState.PauseRequested)
                      _pollerState = PollerState.Running; // applicable if job is long running or no new poll was needed since pause requested
                  else if (_pollerState != PollerState.Paused)
                      _log.WarnFormat("Requested CONTINUE on {0} poller state.", _pollerState);
                  _pauseEvent.Set();
              }
          }
      
          private void DoWork()
          {
              while (_pollerState == PollerState.Running)
              {
                  try
                  {
                      _action();
                  }
                  catch(Exception ex)
                  {
                      _log.Error(Thread.CurrentThread.Name + "failed.", ex);
                  }
                  finally
                  {
                      if (_stopEvent.WaitOne(_pollingInterval))
                      {
                          if (_pollerState == PollerState.StopRequested)
                              _pollerState = PollerState.Stopped;
                      }
      
                      if (_pollerState == PollerState.PauseRequested)
                      {
                          _pollerState = PollerState.Paused;
                          _pauseEvent.Wait();
                          // Continue only if we are still in Pause mode and not StopRequested
                          if (_pollerState == PollerState.Paused)
                              _pollerState = PollerState.Running;
                      }
                  }
              }
              _log.Debug("Exiting: " + Thread.CurrentThread.Name);
          }
      }
      public enum PollerState
      {
          Unstarted = 0,
          Running = 1,
          StopRequested = 2,
          Stopped = 3,
          PauseRequested = 4,
          Paused = 5,
      }
      

      【讨论】:

      • 那么:您可以使用反射将轮询器名称设置为调用者的函数或类或线程名称,从而为您节省轮询器名称参数。您还可以使用 ctor 中的该信息初始化记录器。为了增加轮询器的可重用性,您可能需要将轮询间隔设置移动到启动方法。 start 方法当前不是线程安全的。最后,如果这是我的轮询器,我希望看到调用动作的异常而不是吞下它们。这一切都回答了你的问题吗?如果是,请标记答案。
      • 感谢您的回复,我喜欢我自己的名字,因为它们很短,但在那里使用反射是个好主意 - 将尝试看看它在日志中的外观。您在开始时错过了锁定语句,但仍然存在到期时间过载的问题 - 不幸的是,如果调用两次,将抛出 ThreadStateException 我有自己的 catch 块在操作中以不同方式处理通信异常,如果需要,依此类推。我不希望异常停止我的轮询器。
      • 顺便说一句,你没有处理计时器。而且我不确定在默认的 Start 方法中启动线程两次是否会在不先停止它的情况下工作。而且您不会在任何地方等待线程完成,因此无法保证在您再次调用 Start 之前它已经完成。我想我只是在 Start 上创建一个新的线程对象,我想我以前没有同样的异常。
      • 我从没想过我可以停止并再次启动轮询器,我没有这样的情况,但它会使 threapool 更通用,线程不能再只读 - 无需处理计时器,因为没有
      • 本课程更新 - 您是否进行了任何改进或调整?
      猜你喜欢
      • 2011-07-15
      • 2012-03-24
      • 1970-01-01
      • 2014-09-17
      • 2013-12-23
      • 2016-11-18
      • 1970-01-01
      • 2012-04-20
      • 1970-01-01
      相关资源
      最近更新 更多