Transform Asynchronous method to Synchronous

In the last day, I have found problem to transform an asynchronous call to synchronous. It is not very big problem but this problem is repeated for a big number of methods and components. I think that a good programmer should avoid the copy/paste of code but write a reusable code, for this I write a class that implement a lock/unlock mechanism.
This class has three methods

  • Lock: Lock the object.
  • Unlock: Unlock the object.
  • Wait: Caller remains to wait the Unlock method is called.

The implementation is based over the ReaderWriterLock class, this class protect an enumeration that describe the status of class

Using of SynchronizerObject

The using of SynchronizerObject want is simple. First step in the code that use the SynchroinzerObject is lock the object calling base.Lock() method (the presence of base keyword isn’t mandatory) and check the return value of this method. If this method return true, it’s ok and pass to the second step. The second step is the asynchronous call and after that you should call base.Wait method. This method put the instance of your class to waiting state and exit from this state when is called the base.Unlock method. The call to the Unlock method is the last step and should be made in the asynchronous callback, in this example this instruction is placed in OnMethod method.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.Timers;
   5:  
   6:  namespace It.Matteozan.Sync.Examples
   7:  {
   8:      public class Exposer : SynchronizerObject
   9:      {
  10:          private string str = null;
  11:          private Timer timer;
  12:  
  13:          public string SyncCall()
  14:          {
  15:              if (base.Lock())
  16:              {
  17:  
  18:                  // Make async call
  19:                  timer = new System.Timers.Timer(10000);
  20:  
  21:                  // Hook up the Elapsed event for the timer.
  22:                  timer.Elapsed += new ElapsedEventHandler(OnMethod);
  23:  
  24:                  // Set the Interval to 2 seconds (2000 milliseconds).
  25:                  timer.Interval = 20000;
  26:                  timer.Enabled = true;
  27:  
  28:                  if (base.Wait(25000))
  29:                  {
  30:                      // Read data
  31:                      return str;
  32:                  }
  33:              }
  34:              return "Failed";
  35:          }
  36:  
  37:          #region On Methods
  38:  
  39:          public void OnMethod(object source, ElapsedEventArgs e)
  40:          {
  41:              ((Timer)source).Stop();
  42:  
  43:              str = "Hello...";
  44:              base.Unlock();
  45:          }
  46:  
  47:          #endregion
  48:      }
  49:  }

Implementing SynchronizerObject

This class is centered to manage an enumeration that describes the status of the current instance. To protect this status the access is checked with the ReaderWriterLock
class. ReaderWriterLock implement a read and write lock, in this way the Wait method can read the status and the Unlock method can write the status.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.Threading;
   5:  using System.Diagnostics;
   6:  
   7:  namespace It.Matteozan.Sync
   8:  {
   9:      /// <summary>
  10:      /// This class provided a lock/ulnock status.
  11:      /// </summary>
  12:      public class SynchronizerObject
  13:      {
  14:          #region Enum
  15:  
  16:          private enum SynchronizedObjectStatus
  17:          {
  18:              Living,
  19:              Waiting,
  20:          }
  21:  
  22:          #endregion
  23:  
  24:          #region Fields
  25:  
  26:          /// <summary>
  27:          /// This constant is used to set the dafault timeout to Acquire the Lock.
  28:          /// </summary>
  29:          private static readonly int timeoutToAcquire = -1; // infinite
  30:  
  31:          /// <summary>
  32:          /// This member is the lock on the <code>status</code> member.
  33:          /// </summary>
  34:          private ReaderWriterLock rwLockOnStatus = new ReaderWriterLock();
  35:  
  36:          /// <summary>
  37:          /// This member describes the status of <code>SyncroinzedObject</code>
  38:          /// </summary>
  39:          private SynchronizedObjectStatus status = SynchronizedObjectStatus.Living;
  40:  
  41:          #endregion
  42:  
  43:          #region Private methods
  44:  
  45:          /// <summary>
  46:          /// This method is used to calculate the waiting time of Thread.Sleep.
  47:          /// <remarks>This code should be custumized to meet your requirements</remarks>
  48:          /// </summary>
  49:          /// <param name="timeout"></param>
  50:          /// <param name="msElapsed"></param>
  51:          /// <returns></returns>
  52:          private int CalculateSleepTime(uint timeout, int msElapsed)
  53:          {
  54:              if (msElapsed <= 500)
  55:              {
  56:                  // Context swicht
  57:                  return 0;
  58:              }
  59:              else
  60:              {
  61:                  if (timeout > 0)
  62:                  {
  63:                      if (msElapsed < timeout / 2)
  64:                      {
  65:                          // TODO: Improves
  66:                          return ((int)timeout / 10);
  67:                      }
  68:                      else
  69:                      {
  70:                          return ((int)timeout / 5);
  71:                      }
  72:                  }
  73:                  else
  74:                  {
  75:                      return 500; // 1/2 secs
  76:                  }
  77:              }
  78:          }
  79:  
  80:          #endregion
  81:  
  82:          #region Protected methods
  83:  
  84:          /// <summary>
  85:          /// Lock the instace, in this way this object still waiting Ulock call.
  86:          /// </summary>
  87:          /// <returns>if the Lock method has terminated successfully.</returns>
  88:          protected bool Lock()
  89:          {
  90:              rwLockOnStatus.AcquireWriterLock(timeoutToAcquire);
  91:              try
  92:              {
  93:                  if (status == SynchronizedObjectStatus.Living)
  94:                  {
  95:                      status = SynchronizedObjectStatus.Waiting;
  96:                  }
  97:                  else
  98:                  {
  99:                      return false;
 100:                  }
 101:              }
 102:              finally
 103:              {
 104:                  rwLockOnStatus.ReleaseWriterLock();
 105:              }
 106:  
 107:              return true;
 108:          }
 109:  
 110:          /// <summary>
 111:          /// This method wait to the Ulock call, for a specific amount of time.
 112:          /// </summary>
 113:          /// <param name="timeout">The time that this call remains in waiting. If you use 0 this value is means infinite</param>
 114:          /// <returns>If returns <code>true</code> this method is exeited for the Unlock call, otherwise for an error or timeout.</returns>
 115:          protected bool Wait(uint timeout)
 116:          {
 117:              int currentMs = 0;
 118:              TimeSpan startTime = new TimeSpan(DateTime.Now.Ticks);
 119:              TimeSpan refTime = new TimeSpan(DateTime.Now.Ticks);
 120:  
 121:              bool _continue = true;
 122:              while (_continue)
 123:              {
 124:                  rwLockOnStatus.AcquireReaderLock(timeoutToAcquire);
 125:                  try
 126:                  {
 127:                      currentMs = (int)(refTime.Subtract(startTime).TotalMilliseconds);
 128:                      bool timerCondition = (timeout == 0 || (currentMs < timeout));
 129:                      _continue = (status == SyncronizedObjectStatus.Waiting) && timerCondition;
 130:                  }
 131:                  finally
 132:                  {
 133:                      rwLockOnStatus.ReleaseReaderLock();
 134:                  }
 135:  
 136:  #if DEBUG
 137:                  Debug.WriteLine("Elapsed time " + currentMs.ToString() + " ms");
 138:                  Debug.WriteLine("Thread.Sleep(" + CalculateSleepTime(timeout, currentMs).ToString() + ")");
 139:  #endif
 140:                  Thread.Sleep(CalculateSleepTime(timeout, currentMs));
 141:                  refTime = new TimeSpan(DateTime.Now.Ticks);
 142:              }
 143:  
 144:              rwLockOnStatus.AcquireReaderLock(timeoutToAcquire);
 145:              try
 146:              {
 147:                  if (status == SynchronizedObjectStatus.Living)
 148:                  {
 149:                      return (timeout == 0 || (currentMs < timeout));
 150:                  }
 151:                  else
 152:                  {
 153:                      return false;
 154:                  }
 155:              }
 156:              finally
 157:              {
 158:                  rwLockOnStatus.ReleaseReaderLock();
 159:              }
 160:          }
 161:  
 162:          /// <summary>
 163:          /// This method unlock the object.
 164:          /// </summary>
 165:          /// <returns>If returns <code>true</code>, this method is terminated successful, othewise this method returns <code>false</code></returns>
 166:          protected bool Unlock()
 167:          {
 168:              rwLockOnStatus.AcquireWriterLock(timeoutToAcquire);
 169:              try
 170:              {
 171:                  if (status == SynchronizedObjectStatus.Waiting)
 172:                  {
 173:                      status = SynchronizedObjectStatus.Living;
 174:                  }
 175:                  else
 176:                  {
 177:                      return false;
 178:                  }
 179:              }
 180:              finally
 181:              {
 182:                  rwLockOnStatus.ReleaseWriterLock();
 183:              }
 184:  
 185:              return true;
 186:          }
 187:  
 188:          #endregion
 189:      }
 190:  }

Note: The implementation use ReaderWriterLock but if you can use C# 3.0 you should use ReaderWriterLockSlim in System.Core assembly.

Leave a Reply