同步对由计时器重置的状态变量的访问

时间:2012-11-26 17:03:04

标签: c# timer synchronization

我目前正在使用C#开展项目。我正在使用单个锁同步访问状态变量。触发此状态变量以在给定的时间段内设置,然后应重置其值。我目前的代码如下。

using System.Threading;

class Test
{
  object syncObj = new object();
  bool state = false;
  Timer stateTimer;

  Test()
  {
    stateTimer = new Timer(ResetState, this, Timeout.Infinite, Timeout.Infinite);
  }

  void SetState()
  {
    lock(syncObj)
    {
      state = true;
      stateTimer.Change(1000, Timeout.Infinite);    
    }
  }

  static void ResetState(object o)
  {
    Test t = o as Test;
    lock(t.syncObj)
    {
      t.state = false;
    }
  }  
}

假定在Timer调用ResetState之前再次调用SetState是有效的(即允许延长状态为true的时间段),我可以想象单个锁可能不够的情况。我正在考虑的具体案例是

  • 同时在主线程和Timer线程
  • 上输入SetState和ResetState
  • SetState首先获取锁定并正确地将状态设置为true并触发计时器重新启动
  • ResetState然后错误地将状态设置为false,这意味着状态在预期的时间段内不为真

我一直在摸着这个。我能够解决它的最接近的是使用两个锁,但最后我发现这导致了其他问题(至少,我做的方式)。

有没有一种已知的方法来解决这个问题(我应该阅读一些东西来刷新我的同步知识)?

更新:我忘了提到在这种情况下无法查询定时器的当前状态。如果可以的话,我会想象在ResetState中检查剩余时间以确定计时器是否真的停止了。

1 个答案:

答案 0 :(得分:3)

首先:it's a bad idea to expose the locking object publicly!

class Test
{
  private object syncObj = new object();
  private bool state = false;
  private Timer stateTimer;

  public Test()
  {
    stateTimer = new Timer(ResetState, this, Timeout.Infinite, Timeout.Infinite);
  }

  public void SetState()
  {
    lock(syncObj)
    {
      state = true;
      stateTimer.Change(1000, Timeout.Infinite);    
    }
  }

  public static void ResetState(object o)
  {
    Test t = o as Test;
    t.ResetState();
  }  

由于您不再暴露锁定对象,因此您必须创建另一种方法来重置状态:

  public void ResetState()
  {
    lock(syncObj)
    {
      state = false;
      stateTimer.Change(Timeout.Infinite, Timeout.Infinite);
    }
  }  


}

请注意,我们还会处理新ResetState方法中的另一个问题,那就是强制计时器不要再次触发。这只能保证state标志不会与定时器不同步;即,如果您设置状态,它将保持设置预期的时间量或直到调用重置方法。

更新

如果要拒绝重置尝试,请将状态变量设为枚举:

enum EState
{
    Off = 0,
    On = 1,
    Waiting = 2
}

private EState state = EState.Off;

// Provide a state property to check if the state is on or of (waiting is considered to be Off)
public bool State{ get{ return state == EState.On;} }

此外,您现在需要修改SetState方法,您将需要两个重置方法(私有方法将由计时器使用)。

public void SetState()
{
    lock(syncObj)
    {
        state = EState.Waiting;
        stateTimer.Change(1000, Timeout.Infinite);
    }
}

public void ResetState()
{
    lock(syncObj)
    {
        if(state != EState.Waiting)
        {
            state = EState.Off;
        }
    }
}

private void TimerResetState()
{
    lock(syncObj)
    {
        state = EState.Off;
        stateTimer.Change(Timeout.Infinite, Timeout.Infinite);
    }
}

所以现在你的构造函数看起来像这样:

public Test()
{
    stateTimer = new Timer(TimerResetState, this, Timeout.Infinite, Timeout.Infinite);
}

事情应该大致沿着这些方向发展。