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

时间:2014-10-16 12:38:08

标签: c# multithreading parallel-processing

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

LoopA内部发生的任务可能需要30多个秒才能完成。如果发生这种情况,LoopA将立即重新开始,好像任务只需要20秒,LoopA将不会再运行10秒。

我已经实施了LoopA。我现在的问题是实现LoopB并行运行,其行为方式与LoopA相同,但间隔为5秒。这是我的LoopA代码,它在一个Thread中运行:

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()吗?

3 个答案:

答案 0 :(得分:2)

这是一个你可以放入linqpad并测试的小片段。这将防止计时器重叠。基本上我们执行回调,停止计时器,处理一些工作,重置计时器并再次启动它。你需要一个计时器来完成每个定时任务。

要清楚,这将阻止您的线程不必要地排队,因为您只创建一个新线程来处理另一个完成后的工作。拥有2个计时器将允许您以不同的间隔运行2个不同的进程,从而创建自己的线程。同样,每个计时器只允许每个进程的一个实例发生。

基本流程

  1. 在构造函数中没有间隔的设置计时器
  2. 启动计时器
  3. 计时器等待,当它在新线程上启动进程时
  4. 进程停止计时器
  5. 流程确实有效
  6. 进程设置间隔以重置计时器(由于" AutoReset"属性)
  7. 流程启动计时器
  8. 从第3步开始重复
  9. 基本代码

    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

答案 1 :(得分:2)

        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
                }
            }
        }

答案 2 :(得分:1)

这似乎已经准备好了,但是为了让另一个线程运行,你需要并行启动第一个线程,第二个线程相同,并等待它们。使用Tasks代替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 class更好地服务的实现。请注意,在这种情况下还有不想使用的System.Threading.Timer ,因为它缺少事件模式。