以不同间隔运行的并行循环

本文关键字:并行 循环 运行 | 更新日期: 2023-09-27 18:26:41

在C#中,我正试图创建一个具有两个循环的Windows服务,这两个循环将以设定的间隔同时运行。例如,LoopA将每30秒运行一次,LoopB将每5秒钟运行一次。

LoopA内部的任务可能需要30秒以上才能完成。如果发生这种情况,LoopA将立即重新开始,就像任务只需要20秒一样,LoopB将在10秒内不会再次运行。

我已经实现了LoopA。我现在的问题是实现LoopB并行运行,其行为与LoopA相同,只是间隔5秒。这是我的LoopA代码,它在线程中运行:

private Thread Worker;
private AutoResetEvent StopRequest;
private TimeSpan loop_a_freq;
private TimeSpan loob_b_freq;
public LoopManager(Thread _worker, AutoResetEvent _stopRequest)
{
    // Threads
    this.Worker = _worker;
    this.StopRequest = _stopRequest;
    RunLoopA();
}
private void RunLoopA()
{
    DateTime start_current_loop = DateTime.Now;
    DateTime next_loop = start_current_loop + loop_a_freq;
    for (;;)
    {
        // Do processing here
        new LoopAction();
        DateTime now = DateTime.Now;
        if (now > next_loop)
        {
            next_loop = now;
        }
        TimeSpan delay = next_loop - now;
        Thread.Sleep(delay);
        next_loop += loop_a_freq;
        if (StopRequest.WaitOne(0))
        {
            return;
        }
    }
}

如何实施RunLoopB()?线程是我从未充分探索过的C#的一个方面。我需要使用Parallel.For()吗?

以不同间隔运行的并行循环

这里有一个小片段,您可以将它放入linqpad中并进行测试。这将防止计时器重叠。基本上,我们执行回调,停止计时器,处理一些工作,重置计时器,然后重新启动。每个定时任务都需要一个计时器。

需要明确的是,这将防止您的线程不必要地排队,因为您只创建一个新线程来处理另一个线程完成后的工作。拥有2个定时器将允许您以不同的时间间隔运行2个不同的进程,创建它们自己的线程。同样,每个定时器只允许每个进程发生一个实例。

基本流程

  1. 构造函数中没有间隔的设置计时器
  2. 启动计时器
  3. 计时器等待,然后在经过时在新线程上启动进程
  4. 进程停止计时器
  5. 流程确实有效
  6. 进程设置重置计时器的间隔(由于"自动重置"属性)
  7. 进程启动计时器
  8. 从步骤3开始重复

基本代码

private System.Timers.Timer _timer;
private int _delaySeconds = 5;
void Main()
{
    // timer prevents threads from overlapping modify with care
    _timer = new System.Timers.Timer();
    _timer.Interval = _delaySeconds*1000;
    _timer.AutoReset = true;        // important to prevent overlap
    _timer.Elapsed += OnProcess;
    _timer.Start();
}
private void OnProcess(object sender, System.Timers.ElapsedEventArgs args)
{
    // timer prevents from thread overlapping modify with care
    _timer.Stop();
    // do work
    Debug.WriteLine("Processing on thread. [{0}]", Thread.CurrentThread.ManagedThreadId).Dump("this");
    // setting the interval resets the timer
    _timer.Interval = _delaySeconds*1000;
    _timer.Start();
}

故障排除性能

此外,我昨天发现了这篇文章,它可能对您诊断线程性能有所帮助。

http://msdn.microsoft.com/en-us/library/ee329530.aspx

        using System.Timers;
        enum EventType {quit, task};
        class LoopDaLoop {
            Timer _30Seconds_publiser = new Timer(20000);
            Timer _5Seconds_publisher = new Timer(5000);
            Timer _terminator = new Timer(60000);
            BlockingCollection<EventType> loopAEvents = new BlockingCollection<EventType>();
            BlockingCollection<EventType> loopBEvents = new BlockingCollection<EventType>();
            public LoopDaLoop() {
                _30Seconds_publiser.Elapsed += _30Seconds_publiser_Elapsed;
                _5Seconds_publisher.Elapsed += _5Seconds_publisher_Elapsed;
                _terminator.Elapsed += _terminator_Elapsed;
                _30Seconds_publiser.Start();
                _5Seconds_publisher.Start();
                _terminator.Start();
                Task.Run(() => loopA());
                Task.Run(() => loopB());
            }
            void _terminator_Elapsed(object sender, ElapsedEventArgs e) {
                loopAEvents.Add(EventType.quit);
                loopBEvents.Add(EventType.quit);
            }
            void _5Seconds_publisher_Elapsed(object sender, ElapsedEventArgs e) {
                loopBEvents.Add(EventType.task);
            }
            void _30Seconds_publiser_Elapsed(object sender, ElapsedEventArgs e) {
                loopAEvents.Add(EventType.task);
            }

            private void loopA() {
                while (loopAEvents.Take() != EventType.quit) {
                    // decrypt wife's personal email
                }
            }
            private void loopB() {
                while (loopBEvents.Take() != EventType.quit) {
                    // navigate attack drones
                }
            }
        }

这似乎已经完全准备好了,但为了让另一个线程运行,您需要并行启动第一个线程,第二个线程也是如此,然后等待它们。使用Task s而不是Threads可能更容易,但这里使用的是Thread s

   public LoopManager(Thread _worker, AutoResetEvent _stopRequest)
    {
        // Threads
        this.Worker = _worker;
        this.StopRequest = _stopRequest;
        var threadA = new Thread(RunLoopA);
        var threadB = new Thread(RunLoopB);
        // Start the loops
        threadA.Start();
        threadB.Start();
        // Join the threads to avoid the program exiting before they're done
        threadA.Join();
        threadB.Join();
    }
    private void RunLoopB() {...} // As RunLoopA, but using the loop_b_freq variable

现在,如果您希望能够确保每个循环在不等待上一个进程完成的情况下开始,那么您就开始考虑System.Timers.Timer类更好地提供服务的实现。请注意,还有一个System.Threading.Timer,您不想在这种情况下使用,因为它缺少事件模式。