C#System.Timers.Timer奇怪的行为? [英] C# System.Timers.Timer odd behavior?

查看:201
本文介绍了C#System.Timers.Timer奇怪的行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是编写一个代码段,使我可以在并发环境中对对象(例如txt文件)进行独占访问.考虑到这一点,我正在测试基于使用两个System.Timers计时器构建的简单程序.两个计时器的事件处理程序共享相同的锁定对象(请参见下面的代码).
定时器以不同的间隔同时启动,定时器1为3秒,定时器2为1秒.

















令我感到惊讶的是,当锁释放时,我没有将所有事件堆叠在内存中的timer2事件中(仅应用程序.其他事件).我以为,虽然timer1的事件处理程序具有锁定,但timer2的事件仍在内存中堆积.但这显然是不正确的.为什么某些Timer2事件消失了?

My goal is to write a code snippet that lets me have an exclusive access to an object (f.ex a txt file) in concurrent environment. With this in mind I was testing the simple program built on use of two System.Timers timers. Event handlers of both timers share the same lock object (Please see the code below).
The timers start simultaneously with different interval, 3s for timer1 and 1s for timer2. Timer1 supposed to work only for one cycle, during which it's event handler will sleep for 10s and thus keeping the lock.
What's surprised me is that when the lock released, I don't get all stacked in memory timer2 events (only app. every other of them). I thought, while timer1's event handler has the lock, timer2's events are stacking in memory. But that's apparently not true. Why some timer2 events dissappear?

class Program
{
    static int counter = 0;
    static readonly object locker = new object();
    System.Timers.Timer timer1;
    System.Timers.Timer timer2;

    static void Main(string[] args)
    {
        Program p = new Program();

        p.timer1 = new System.Timers.Timer(3000);
        p.timer1.Elapsed += new ElapsedEventHandler(p.Timer1EventHandler);
        p.timer1.Start();
        p.timer2 = new System.Timers.Timer(1000);
        p.timer2.Elapsed += new ElapsedEventHandler(p.Timer2EventHandler);
        p.timer2.Start();
        ThreadPool.SetMaxThreads(50, 50);
        Console.ReadLine();
    }

    void Timer1EventHandler(object sender, ElapsedEventArgs e)
    {
        timer1.Stop();
        DoThingsForTimer1Event();
    }

    void DoThingsForTimer1Event()
    {
        lock (locker)
        {
            Console.WriteLine(DateTime.Now + " Timer1 event started." + " Current thread number " + Thread.CurrentThread.ManagedThreadId);

            Thread.Sleep(10000);

            Console.WriteLine(DateTime.Now + " Timer1 event finished. Lock released.");
        }

    }

    void Timer2EventHandler(object sender, ElapsedEventArgs e)
    {
        counter++;
        lock (locker)
        {
            Console.WriteLine(DateTime.Now + " Timer2 event fired. Current thread number " + Thread.CurrentThread.ManagedThreadId +
                " Counter=" + counter);
        }                                         
    }
}

推荐答案

感谢@TheGeneral将其识别为OP问题的根本原因.

这里遇到的主要问题是,由于您的CPU只有4个逻辑核,因此您的ThreadPool已用光(并且Timer正在使用ThreadPool).这解释了为什么我个人(具有12个核心)无法重现此内容.

The main issue you are running into here is that your ThreadPool is exhausted (and your Timer is using the ThreadPool), since you have a CPU with only 4 logical cores. This explains why I personally (with 12 cores) am unable to reproduce this.

按照

默认情况下,最小线程数设置为系统上的处理器数.

By default, the minimum number of threads is set to the number of processors on a system.

因此线程池调度程序可能从4个线程开始.线程池调度程序非常保守.它不只是按照您的要求分配线程-有时

So the thread pool scheduler is likely starting with 4 threads. The thread pool scheduler is quite conservative. It doesn't just dish out threads as you ask for them - it sometimes delays creating them to aid in overall system performance (since spinning up threads is expensive).

要解决您的立即问题,您可以使用以下命令提示线程池更快地启动更多线程:

To fix your immediate issue, you can prompt the thread pool to spin up more threads more quickly, using:

ThreadPool.SetMinThreads(50, 50);

这将使它迅速增加到50,然后在此之后更加保守.

This will ramp it quickly to 50, and then more conservatively after that.

从长远来看,问题在于您正在线程池中进行长时间运行的操作.这是一个坏主意.您可能希望将它们移至线程,或移至长期运行的任务(实际上是线程).但是,这两种选择都有其缺点.从根本上说,如果可能的话,您希望在线程池之外保留​​长时间运行的操作.

Longer term though, the issue is that you are doing long running operations in the thread pool. This is a bad idea. You may wish to move them to threads, or to long running tasks (which, in practice, are threads). But both of those options have their downside. Fundamentally, you want to keep long running operations outside of the thread pool if possible.

如果不了解为什么要使用lock,则很难给出很好的建议.但是要考虑的一个选项可能是使用BlockingCollection形成队列-然后使用单个单独的线程来处理该队列.这意味着您的Timer事件只会将一个条目添加到队列中,然后返回-处理的重点将在(单个)线程中,该线程正在处理队列中的条目.

Without understanding why you are using lock it is hard to give great advice. But one option to consider might be to use a BlockingCollection to form a queue - and then have a single separate thread processing that queue. This means your Timer events will just add an entry to the queue and then return - the brunt of the processing will be in the (single) thread that is processing the entries from the queue.

这篇关于C#System.Timers.Timer奇怪的行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆