处置后监控多个 Threading.Timers

发布于 2024-11-17 08:12:59 字数 2926 浏览 2 评论 0原文

我有一个进程,它创建一个动态计时器列表(System.Threading.Timer)并继续运行,直到收到终止信号。一旦收到终止信号,我希望任何现有的计时器回调完成(见下文):

private IList<Timer> _timers = new List<Timer>();
...
...
private void WaitOnExecutingThreads()
{
  var waiters = new List<ManualResetEvent>(_timers.Count);

  foreach (var timer in _timers)
  {
      var onWait = new ManualResetEvent(false);
      waiters.Add(onWait);
      timer.Dispose(onWait);
  }

  WaitHandle.WaitAll(waiters.ToArray());
  waiters.ForEach(x=> x.Dispose());
}

此代码现在可以工作,但我想在计时器被处理后监视正在进行的线程回调。我的目的是按照给定的时间间隔写入日志“计时器 A 仍在运行”。

我开始玩:

ThreadPool.RegisterWaitForSingleObject(....)

添加添加以下内容: (注意:我创建了一个 ThreadContext 类,其中包含计时器和关联数据)

private void WaitOnExecutingThreads()
{
   var waiters = new List<ManualResetEvent>();

   WaitOrTimerCallback IsRunning = (x, timeout) => { if (timeout) { Log(x + "is still    running"); } };

   foreach (var threadContext in _threadContexts)
   {
      var onWait = new ManualResetEvent(false);
      threadContext.Timer.Dispose(onWait);

      ThreadPool.RegisterWaitForSingleObject(onWait, IsRunning , threadContext.ThreadInfo.Id, new TimeSpan(0, 0, 30), false);

      waiters.Add(onWait);
   }

        WaitHandle.WaitAll(waiters.ToArray());
        waiters.ForEach(x=> x.Dispose());
    }

我觉得这应该是 C# .net 4.0 中的一项直接任务。在我的简单单元测试中,我的 IsRunning 回调在等待后会触发相当多的时间。此调用后我不再执行任何进一步的执行。但我正在编写相当多的代码,我不太适应并且感觉这会失败。

有没有更简单的解决方案或者我误解了什么?

更新 根据 Peter R. 的建议,我提出了以下建议。授予它更多的代码行,但我不必注册单个线程对象。如果所有线程在处理后仍在执行,我会休眠 10 秒,然后再次检查此示例。

    private void WaitOnExecutingThreads()
    {
        foreach (var threadContext in _threadContexts)
        {
            threadContext.DisposeWaiter = new ManualResetEvent(false);
            threadContext.Timer.Dispose(threadContext.DisposeWaiter);
        }

        while(_threadContexts.Count > 0)
        {
            for(var i = 0; i < _threadContexts.Count; i++)
            {
                var threadContext = _threadContexts[i];
                var isComplete = threadContext.DisposeWaiter.WaitOne(0);
                if(isComplete)
                {
                    Console.WriteLine(string.Format("{0}: {1} has completed", DateTime.Now, threadContext.Name));
                    _threadContexts.RemoveAt(i);
                }
                else
                {
                    Console.WriteLine(string.Format("{0}: {1} is still running", DateTime.Now, threadContext.Name));
                }
            }

            if (_threadContexts.Count > 0)
            {
                Thread.Sleep(new TimeSpan(0, 0, 10));
            }
        }
    }
....
public class ThreadContext
{
    public string Name { get; set; }
    public Timer Timer { get; set; }
    public WaitHandle DisposeWaiter { get; set; }
}

_

I have a process, which creates a dynamic list of timers(System.Threading.Timer) and continues to run until a signal is received to terminate. Once a signal is received to terminate I want any existing timer callbacks to complete (See Below):

private IList<Timer> _timers = new List<Timer>();
...
...
private void WaitOnExecutingThreads()
{
  var waiters = new List<ManualResetEvent>(_timers.Count);

  foreach (var timer in _timers)
  {
      var onWait = new ManualResetEvent(false);
      waiters.Add(onWait);
      timer.Dispose(onWait);
  }

  WaitHandle.WaitAll(waiters.ToArray());
  waiters.ForEach(x=> x.Dispose());
}

This code works right now, but I would like to monitor the ongoing thread callbacks once the timers are disposed. My intent is to write to a log at a given interval "Timer A is still running".

I started playing with:

ThreadPool.RegisterWaitForSingleObject(....)

add added the following:
(Note:I created a class ThreadContext which contains the timer and associated data)

private void WaitOnExecutingThreads()
{
   var waiters = new List<ManualResetEvent>();

   WaitOrTimerCallback IsRunning = (x, timeout) => { if (timeout) { Log(x + "is still    running"); } };

   foreach (var threadContext in _threadContexts)
   {
      var onWait = new ManualResetEvent(false);
      threadContext.Timer.Dispose(onWait);

      ThreadPool.RegisterWaitForSingleObject(onWait, IsRunning , threadContext.ThreadInfo.Id, new TimeSpan(0, 0, 30), false);

      waiters.Add(onWait);
   }

        WaitHandle.WaitAll(waiters.ToArray());
        waiters.ForEach(x=> x.Dispose());
    }

I feel like this should be a straight forward task in C# .net 4.0. In my simple unit test, My IsRunning callback fires quite a bit after the wait. I do not perform any further execution after this call. but I am writing quite a bit of code that I am not too comfortable with and feel like this will fail.

Is there a simpler solution or I am misunderstanding something?

UPDATE
Based on Peter R. suggestion I came up with the following below. Granted its more lines of code but I don't have to register a single thread object. If all the threads are still executing after disposal I sleep for 10 seconds and check again for this example.

    private void WaitOnExecutingThreads()
    {
        foreach (var threadContext in _threadContexts)
        {
            threadContext.DisposeWaiter = new ManualResetEvent(false);
            threadContext.Timer.Dispose(threadContext.DisposeWaiter);
        }

        while(_threadContexts.Count > 0)
        {
            for(var i = 0; i < _threadContexts.Count; i++)
            {
                var threadContext = _threadContexts[i];
                var isComplete = threadContext.DisposeWaiter.WaitOne(0);
                if(isComplete)
                {
                    Console.WriteLine(string.Format("{0}: {1} has completed", DateTime.Now, threadContext.Name));
                    _threadContexts.RemoveAt(i);
                }
                else
                {
                    Console.WriteLine(string.Format("{0}: {1} is still running", DateTime.Now, threadContext.Name));
                }
            }

            if (_threadContexts.Count > 0)
            {
                Thread.Sleep(new TimeSpan(0, 0, 10));
            }
        }
    }
....
public class ThreadContext
{
    public string Name { get; set; }
    public Timer Timer { get; set; }
    public WaitHandle DisposeWaiter { get; set; }
}

_

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

留蓝 2024-11-24 08:12:59

如果您的处理程序尚未完成,您的 ManualResetEvents 将不会收到信号。因此,您可以简单地测试事件是否处于有信号状态。 IE

var isComplete = waiters[0].WaitOne(0);

If your handlers haven't completed, your ManualResetEvents will not be signalled. So, you could simply test if the event is in a signaled state or not. i.e.

var isComplete = waiters[0].WaitOne(0);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文