定时器初始化和竞争条件在C#?定时器、初始化、条件、竞争

2023-09-07 14:59:16 作者:忆·倾城

我看到了里希特的这本书code:

  

下面code演示了如何有一个线程池线程调用   立即启动方法,然后每2秒后:

  / * 1 * /静态内部类TimerDemo
/ * 2 * / {
/ * 3 * /私有静态定时s_timer;
/ * 4 * /公共静态无效的主要()
/ * 5 * / {
/ * 6 * / Console.WriteLine(检查状态每2秒);
/ * 7 * / //创建一个定时器,确保它永远不会触发。这确保了
/ * 8 * / // s_timer是指之前的状态是由一个线程池线程调用
/ * 9 * / s_timer =新的定时器(状态,空,Timeout.Infinite,Timeout.Infinite);
/ * 10 * / //现在s_timer被分配到,我们可以让计时器火灾知道
/ * 11 * / //在调用状态的变化将不会抛出一个NullReferenceException
/ * 12 * / s_timer.Change(0,Timeout.Infinite);
/ * 13 * /到Console.ReadLine(); // prevent从终止进程
/ * 14 * /}
/ * 15 * / //此方法的签名必须与TimerCallback委托匹配
/ * 16 * /私有静态无效状态(对象状态)
/ * 17 * / {
/ * 18 * / //此方法是通过一个线程池线程中执行
/ * 20 * / Console.WriteLine(在{0}状态,DateTime.Now);
/ * 21 * / Thread.sleep代码(1000); //模拟等工作(1秒)
/ * 22 *​​ / //只返回前,又有定时器火2秒
/ * 23 * / s_timer.Change(2000,Timeout.Infinite);
/ * 24 * / //此方法返回时,线程再次进入
/ * 25 * / //游泳池和等待另一个工作项目
/ * 26 * /}
/ * 27 * /}
 

不过,(对不起),我还是不明白什么行#7,#8 办法

当然 - 为什么它初始化(行#9) Timeout.Infinite (这显然是:的不启动定时器)

(我明白了preventing重叠通用,但我相信也有一个 GC 竞争条件过氧化值在这里。)的

修改

命名空间是的System.Threading

解决方案

我认为这是不相关的GC,而是要避免竞争条件

分配操作不是原子操作:首先创建Timer对象,那么你为它分配

因此​​,这里是一个场景:

新的定时器(...)创建定时器并开始计数

当前线程pempted分配端 => s_timer 在 $ P $仍然空

在计时器上的另一个线程醒来并调用状态但最初的线程尚未完成的赋值操作!

状态访问 s_timer 这是空引用 => < STRONG> BOOM!

使用他的方法就不可能发生,例如,用同样的情形:

创建了计时器,但不启动

在当前线程是preempted

什么也没有发生由于计时器尚未开始募集活动

初始线程再次运行

它的结束赋值 => s_timer 引用的计时器

在定时器安全启动:以状态未来的呼叫是有效的,因为 s_timer 是一个有效的参考

简单易制的照明节能装置

I saw this code on Richter's book :

The following code demonstrates how to have a thread pool thread call a method starting immediately and then every 2 seconds thereafter:

/*1*/    internal static class TimerDemo
/*2*/    {
/*3*/        private static Timer s_timer;
/*4*/        public static void Main()
/*5*/        {
/*6*/            Console.WriteLine("Checking status every 2 seconds");
/*7*/            // Create the Timer ensuring that it never fires. This ensures that
/*8*/            // s_timer refers to it BEFORE Status is invoked by a thread pool thread
/*9*/            s_timer = new Timer(Status, null, Timeout.Infinite, Timeout.Infinite);
/*10*/            // Now that s_timer is assigned to, we can let the timer fire knowing
/*11*/            // that calling Change in Status will not throw a NullReferenceException
/*12*/            s_timer.Change(0, Timeout.Infinite);
/*13*/            Console.ReadLine(); // Prevent the process from terminating
/*14*/        }
/*15*/        // This method's signature must match the TimerCallback delegate
/*16*/        private static void Status(Object state)
/*17*/        {
/*18*/            // This method is executed by a thread pool thread
/*20*/            Console.WriteLine("In Status at {0}", DateTime.Now);
/*21*/            Thread.Sleep(1000); // Simulates other work (1 second)
/*22*/            // Just before returning, have the Timer fire again in 2 seconds
/*23*/            s_timer.Change(2000, Timeout.Infinite);
/*24*/            // When this method returns, the thread goes back
/*25*/            // to the pool and waits for another work item
/*26*/        }
/*27*/    }

However , (sorry) , I still don't understand what lines #7,#8 means

And of course - why was it initialized (line #9) to Timeout.Infinite ( which is obviously : "don't start the timer")

( I do understand the general purpose for preventing overlaps , but I believe there is also a GC race condition pov here.)

edit

the namespace is System.Threading

解决方案

I think it's not related to the GC but rather to avoid a race condition:

The assignment operation is not atomic: first you create the Timer object then you assign it.

So here is a scenario:

new Timer(...) creates the timer and it starts "counting"

the current thread is preempted BEFORE the assignment ends => s_timer is still null

the timer wakes up on another thread and calls Status but the initial thread has not yet finished the assignment operation!

Status accesses s_timer which is a null reference => BOOM!

With his method it can't happen, e.g. with the same scenario:

the timer is created but does not start

the current thread is preempted

nothing happens because the timer has not yet started to raise events

the initial thread is running again

it ends the assignment => s_timer references the timer

the timer is started safely: any future call to Status is valid because s_timer is a valid reference