【问题标题】:Monitor multiple Threading.Timers after Disposal处理后监控多个 Threading.Timers
【发布时间】:2011-06-27 23:59:29
【问题描述】:

我有一个进程,它创建一个动态的计时器列表(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(....)

add 添加了以下内容: (注意:我创建了一个包含计时器和相关数据的类 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; }
}

_

【问题讨论】:

    标签: c# .net multithreading timer thread-safety


    【解决方案1】:

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

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

    【讨论】:

    • 嗨,Peter,我可以使用 ThreadPool.RegisterWaitForSingleObject 来验证回调是否完成。你是说我应该在我的 IsRunning 委托中使用上面的代码吗?我正在考虑改用 System.Timer,但我还没有玩过它。我真的不喜欢调用 ThreadPool.RegisterWaitForSingleObject。这似乎是很多开销。
    • 参数为 0 的 WaitOne 调用并没有真正等待。即它等待 0 MS,这意味着它实际上只是为您提供运行状态...
    • 嗨彼得,回到这个,我可以清楚地看到你的建议。我挂断了 RegisterWaitForSingleObject 电话。我已经收集了等待事件,然后我可以继续检查谁仍在主线程中运行。这将摆脱 WaitHandle.WaitAll 调用,使其更加简单。
    • 除非您想更新答案,否则我将发布解决方案的更新。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多