[ACCEPTED]-Is there a synchronization class that guarantee FIFO order in C#?-synchronization

Accepted answer
Score: 40

You'll need to write your own class to do 3 this, I found this example (pasted because 2 it looks as though the site's domain has 1 lapsed):

using System.Threading;

public sealed class QueuedLock
{
    private object innerLock;
    private volatile int ticketsCount = 0;
    private volatile int ticketToRide = 1;

    public QueuedLock()
    {
        innerLock = new Object();
    }

    public void Enter()
    {
        int myTicket = Interlocked.Increment(ref ticketsCount);
        Monitor.Enter(innerLock);
        while (true)
        {

            if (myTicket == ticketToRide)
            {
                return;
            }
            else
            {
                Monitor.Wait(innerLock);
            }
        }
    }

    public void Exit()
    {
        Interlocked.Increment(ref ticketToRide);
        Monitor.PulseAll(innerLock);
        Monitor.Exit(innerLock);
    }
}

Example of usage:

QueuedLock queuedLock = new QueuedLock();

try
{
   queuedLock.Enter();
   // here code which needs to be synchronized
   // in correct order
}
finally
{
    queuedLock.Exit();
}

Source via archive.org

Score: 13

Just reading Joe Duffy's "Concurrent Programming 21 on Windows" it sounds like you'll usually get FIFO 20 behaviour from .NET monitors, but there 19 are some situations where that won't occur.

Page 18 273 of the book says: "Because monitors 17 use kernel objects internally, they exhibit 16 the same roughly-FIFO behavior that the 15 OS synchronization mechanisms also exhibit 14 (described in the previous chapter). Monitors 13 are unfair, so if another thread sneaks 12 in and acquires the lock before an awakened 11 waiting thread tries to acquire the lock, the 10 sneaky thread is permitted to acquire the 9 lock."

I can't immediately find the section 8 referenced "in the previous chapter" but 7 it does note that locks have been made deliberately 6 unfair in recent editions of Windows to 5 improve scalability and reduce lock convoys.

Do you definitely 4 need your lock to be FIFO? Maybe there's 3 a different way to approach the problem. I 2 don't know of any locks in .NET which are 1 guaranteed to be FIFO.

Score: 8

You should re-design your system to not 10 rely on the execution order of the threads. For 9 example, rather than have your threads make 8 a DB call that might take more than one 7 second, have your threads place the command 6 they would execute into a data structure 5 like a queue (or a heap if there is something 4 that says "this one should be before another 3 one"). Then, in spare time, drain the queue 2 and do your db inserts one at a time in 1 the proper order.

Score: 0

There is no guaranteed order on any built-in 10 synchronisation objects: http://msdn.microsoft.com/en-us/library/ms684266(VS.85).aspx

If you want a guaranteed 9 order you'll have to try and build something 8 yourself, note though that it's not as easy 7 as it might sound, especially when multiple 6 threads reach the synchronisation point 5 at (close to) the same time. To some extent 4 the order they will be released will always 3 be 'random' since you cannot predict in 2 which order the point is reached, so does 1 it really matter?

Score: 0

Actually the answers are good, but I solved 3 the problem by removing the timer and run 2 the method (timer-handler previously) into 1 background thread as follows

    private void InsertBasicVaraibles()
    {
         int functionStopwatch = 0;
         while(true)
         {

           try
           {
             functionStopwatch = Environment.TickCount;
             DataTablesMutex.WaitOne();//mutex for my shared resources
             //insert into DB 
           }
           catch (Exception ex)
           {
             //Handle            
           }
           finally            
           {                
              DataTablesMutex.ReleaseMutex();
           }

           //simulate the timer tick value
           functionStopwatch = Environment.TickCount - functionStopwatch;
           int diff = INSERTION_PERIOD - functionStopwatch;
           int sleep = diff >= 0 ?  diff:0;
           Thread.Sleep(sleep);
        }
    }
Score: 0

Follow up on Matthew Brindley's answer.

If 6 converting code from

lock (LocalConnection.locker) {...}

then you could either 5 do a IDisposable or do what I did:

public static void Locking(Action action) {
  Lock();
  try {
    action();
  } finally {
    Unlock();
  }
}

LocalConnection.Locking( () => {...});

I decided 4 against IDisposable because it would creates 3 a new invisible object on every call.

As 2 to reentrancy issue I modified the code 1 to this:

public sealed class QueuedLock {
    private object innerLock = new object();
    private volatile int ticketsCount = 0;
    private volatile int ticketToRide = 1;
    ThreadLocal<int> reenter = new ThreadLocal<int>();

    public void Enter() {
        reenter.Value++;
        if ( reenter.Value > 1 ) 
            return;
        int myTicket = Interlocked.Increment( ref ticketsCount );
        Monitor.Enter( innerLock );
        while ( true ) {
            if ( myTicket == ticketToRide ) {
                return;
            } else {
                Monitor.Wait( innerLock );
            }
        }
    }

    public void Exit() {
        if ( reenter.Value > 0 ) 
            reenter.Value--;
        if ( reenter.Value > 0 ) 
            return;
        Interlocked.Increment( ref ticketToRide );
        Monitor.PulseAll( innerLock );
        Monitor.Exit( innerLock );
    }
}
Score: 0

In case anyone needs Matt's solution in 1 F#

    type internal QueuedLock() =
        let innerLock = Object()
        let ticketsCount = ref 0
        let ticketToRide = ref 1
        member __.Enter () =
            let myTicket = Interlocked.Increment ticketsCount
            Monitor.Enter innerLock
            while myTicket <> Volatile.Read ticketToRide do
                Monitor.Wait innerLock |> ignore
        member __.Exit () =
            Interlocked.Increment ticketToRide |> ignore
            Monitor.PulseAll innerLock
            Monitor.Exit innerLock

More Related questions