我有一个场景,我的C#类有两个方法DoThis()DoThat(),它们被外部调用者以任意顺序相互独立地调用。这两种方法需要通过以下方式进行同步:

  • 调用DoThis()后,至少等待t1秒,然后再执行DoThat()执行
  • 调用DoThat()后,至少等待t2秒,然后再执行DoThis()执行

  • 所以本质上是伪代码:
    static SomeCustomTimer Ta, Tb;
    static TimeSpan t1, t2;
    
    public static void DoThis()
    {
        if(Tb.IsRunning())
            Tb.WaitForExpiry();
    
        DoStuff();
        Ta.Start(t1);
    }
    
    public static void DoThat()
    {
        if(Ta.IsRunning())
            Ta.WaitForExpiry();
    
        DoOtherStuff();
        Tb.Start(t2);
    }
    
    DoStuff()DoOtherStuff()不是长期运行的方法,否则不会共享资源。通常,不会同时调用DoThis()DoThat()。但是我仍然需要防止潜在的僵局。

    如何最好地在C#中实现DoThis()DoThat()

    编辑
    我现在的情况很简单,因为没有任意数量的线程调用这些函数。为了简化起见,有一个调用程序线程以任意顺序调用这些函数。因此,不会同时调用这两个方法,而是调用者将以任意顺序一对一地调用这些方法。我无法控制调用者线程的代码,因此我想强制连续两次调用DoThis(),DoThat()之间的延迟。

    最佳答案

    使用定时锁存器很容易解决这个问题。闩锁是打开或关闭的同步机制。允许开放线程通过时。当封闭的线程无法通过时。定时锁存器是经过一定时间后将自动重新打开或重新关闭的锁存器。在这种情况下,我们需要一个“常开”锁存器,以便使行为偏向于保持打开状态。这意味着在超时后,闩锁将自动重新打开,但仅在显式调用Close的情况下才会关闭。多次调用Close将重置计时器。

    static NormallyOpenTimedLatch LatchThis = new NormallyOpenTimedLatch(t2);
    static NormallyOpenTimedLatch LatchThat = new NormallyOpenTimedLatch(t1);
    
    static void DoThis()
    {
      LatchThis.Wait();  // Wait for it open.
    
      DoThisStuff();
    
      LatchThat.Close();
    }
    
    static void DoThat()
    {
      LatchThat.Wait(); // Wait for it open.
    
      DoThatStuff();
    
      LatchThis.Close();
    }
    

    我们可以像下面这样实现定时锁存器。
    public class NormallyOpenTimedLatch
    {
        private TimeSpan m_Timeout;
        private bool m_Open = true;
        private object m_LockObject = new object();
        private DateTime m_TimeOfLastClose = DateTime.MinValue;
    
        public NormallyOpenTimedLatch(TimeSpan timeout)
        {
            m_Timeout = timeout;
        }
    
        public void Wait()
        {
            lock (m_LockObject)
            {
                while (!m_Open)
                {
                    Monitor.Wait(m_LockObject);
                }
            }
        }
    
        public void Open()
        {
            lock (m_LockObject)
            {
                m_Open = true;
                Monitor.PulseAll(m_LockObject);
            }
        }
    
        public void Close()
        {
            lock (m_LockObject)
            {
                m_TimeOfLastClose = DateTime.UtcNow;
                if (m_Open)
                {
                    new Timer(OnTimerCallback, null, (long)m_Timeout.TotalMilliseconds, Timeout.Infinite);
                }
                m_Open = false;
            }
        }
    
        private void OnTimerCallback(object state)
        {
            lock (m_LockObject)
            {
                TimeSpan span = DateTime.UtcNow - m_TimeOfLastClose;
                if (span > m_Timeout)
                {
                    Open();
                }
                else
                {
                    TimeSpan interval = m_Timeout - span;
                    new Timer(OnTimerCallback, null, (long)interval.TotalMilliseconds, Timeout.Infinite);
                }
            }
        }
    
    }
    

    09-25 17:45