How to implement synchronization with timers in C #

I have a scenario where my C # class has two methods: DoThis() and DoThat() , which are called independent of each other in any order by an external caller. These two methods must be synchronized as follows:

  • After calling DoThis() wait at least t1 seconds before continuing with DoThat()
  • After calling DoThat() wait at least t2 seconds before continuing with DoThis()

So essentially in the pseudocode:

 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() and DoOtherStuff() are non -durable methods and do not exchange resources otherwise. Normally, DoThis() and DoThat() will not be called at the same time. But I still need to protect against potential deadlocks.

What is the best way to implement DoThis() , DoThat() in C #?

EDIT My script right now is simple in that there is no arbitrary number of threads calling these functions. To simplify, there is one call flow that calls these functions in random order. Thus, two methods will not be called at the same time; instead, the caller will call these methods one at a time in any order. I have no control over the caller's thread code, so I want to provide a delay between successive calls to DoThis (), DoThat ().

+4
source share
3 answers

This is pretty easy to solve with a temporary latch. The latch is a synchronization mechanism that is either open or closed. When open threads are allowed. When closed threads cannot pass. A temporary latch is one that will automatically open or resume after a certain amount of time. In this case, we want a โ€œnormally openโ€ latch, so the bias tends to remain open. This means that the latch will automatically open after a timeout, but will close only if Close explicitly called. Several Close calls will reset the timer.

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

And we can implement our temporary latch as shown below.

 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); } } } } 
+5
source

Hm .. What do you need in this case: One thread calls DoThis for some time in a row. Can another start DoThat at least t2 seconds after the LAST call to DoThat or the first after the last call to DoThat?

I think that if your target platform is Win, then it is better to use WaitableTimer (although it is not implemented in .NET, but you can use it through the API. You need to define these functions:

 [DllImport("kernel32.dll")] public static extern IntPtr CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName); [DllImport("kernel32.dll")] public static extern bool SetWaitableTimer(IntPtr hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume); [DllImport("kernel32", SetLastError = true, ExactSpelling = true)] public static extern Int32 WaitForSingleObject(IntPtr handle, int milliseconds); public static uint INFINITE = 0xFFFFFFFF; 

And then using this as follows:

 private IntPtr _timer = null; //Before first call of DoThis or DoThat you need to create timer: //_timer = CreateWaitableTimer (IntPtr.Zero, true, null); public static void DoThis() { //Waiting until timer signaled WaitForSingleObject (_timer, INFINITE); DoStuff(); long dueTime = 10000 * 1000 * seconds; //dueTime is in 100 nanoseconds //Timer will signal once after expiration of dueTime SetWaitableTimer (_timer, ref dueTime, 0, IntPtr.Zero, IntPtr.Zero, false); } public static void DoThis() { //Waiting until timer signaled WaitForSingleObject (_timer, INFINITE); DoOtherStuff(); long dueTime = 10000 * 1000 * seconds; //dueTime is in 100 nanoseconds //Timer will signal once after expiration of dueTime SetWaitableTimer (_timer, ref dueTime, 0, IntPtr.Zero, IntPtr.Zero, false); } 

And after use, you can destroy the timer by calling CloseHandle.

+1
source

Ok, I am trying to solve this problem using EventWaitHandle. Search for comments / reviews. Can it work reliably?

 // Implementation of a manual event class with a DelayedSet method // DelayedSet will set the event after a delay period // TODO: Improve exception handling public sealed class DelayedManualEvent : EventWaitHandle { private SysTimer timer; // using SysTimer = System.Timers.Timer; public DelayedManualEvent() : base(true, EventResetMode.ManualReset) { timer = new SysTimer(); timer.AutoReset = false; timer.Elapsed +=new ElapsedEventHandler(OnTimeout); } public bool DelayedSet(TimeSpan delay) { bool result = false; try { double timeout = delay.TotalMilliseconds; if (timeout > 0 && timer != null && Reset()) { timer.Interval = timeout; timer.Start(); result = true; Trace.TraceInformation("DelayedManualEvent.DelayedSet Event will be signaled in {0}ms", delay); } } catch (Exception e) { Trace.TraceError("DelayedManualEvent.DelayedSet Exception {0}\n{1}", e.Message, e.StackTrace); } return result; } private void OnTimeout(object source, ElapsedEventArgs e) { if (timer != null) { timer.Stop(); Trace.TraceInformation("DelayedManualEvent.OnTimeout Event signaled at time {0}", e.SignalTime); } try { if (!Set()) { Trace.TraceError("DelayedManualEvent.OnTimeout Event set failed"); } } catch (Exception ex) { Trace.TraceError("DelayedManualEvent.OnTimeout Exception in signaling event\n{0}]\n{1}", ex.Message, ex.StackTrace); } } protected override void Dispose(bool disposing) { if (timer != null) { timer.Dispose(); } base.Dispose(disposing); } } 

How do I plan to use this:

 // Pseudocode static DelayedManualEvent delayedEvent = new DelayedManualEvent(); static TimeSpan t1, t2, maxTimeout; public static void DoThis() { if(!delayedEvent.WaitOne(maxTimeout)) return; DoStuff(); delayedEvent.DelayedSet(t1); } public static void DoThat() { if(!delayedEvent.WaitOne(maxTimeout)) return; DoOtherStuff(); delayedEvent.DelayedSet(t2); } 
0
source

All Articles