2

現在、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 である期間を延長できること) を考えると、単一のロックでは不十分な状況が想像できます。私が考えている具体的なケースはこれです

  • SetState と ResetState の両方が、それぞれメイン スレッドとタイマー スレッドで同時に入力されます。
  • SetState は最初にロックを取得し、状態を正しく true に設定し、タイマーをトリガーして再度開始します。
  • その後、ResetState は誤って状態を false に設定します。これは、予想される期間、状態が true ではないことを意味します。

私はしばらくの間、これについて頭を悩ませてきました。それを解決できるようになった最も近い方法は、2 つのロックを使用することでしたが、最終的には、これが他の問題を引き起こしていることがわかりました (少なくとも、私が行った方法では)。

この問題を解決する既知の方法はありますか (また、同期に関する知識を更新するために何かを読む必要があります)。

更新: このインスタンスでは、タイマーの現在の状態を照会できないことを忘れていました。可能であれば、ResetState の残り時間をチェックして、タイマーが本当に停止しているかどうかを判断することを想像できます。

4

1 に答える 1

3

何よりもまず、ロックしているオブジェクトを公に公開するのは悪い考えです!

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 メソッドを変更する必要があり、2 つのリセット メソッドが必要になります (プライベート メソッドはタイマーで使用されます)。

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

物事はこれらの線に沿って大まかに機能するはずです。

于 2012-11-26T19:44:09.020 に答える