Is there a synchronization class that guarantee FIFO order in C#?

前端 未结 7 1549
无人共我
无人共我 2020-11-28 11:01

What is it and how to use?

I need that as I have a timer that inserts into DB every second, and I have a shared resource between timer handler and the main thread. I

相关标签:
7条回答
  • 2020-11-28 11:15

    Follow up on Matthew Brindley's answer.

    If converting code from

    lock (LocalConnection.locker) {...}
    

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

    public static void Locking(Action action) {
      Lock();
      try {
        action();
      } finally {
        Unlock();
      }
    }
    
    LocalConnection.Locking( () => {...});
    

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

    As to reentrancy issue I modified the code 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 );
        }
    }
    
    0 讨论(0)
  • 2020-11-28 11:19

    You'll need to write your own class to do this, I found this example (pasted because it looks as though the site's domain has 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

    0 讨论(0)
  • 2020-11-28 11:20

    In case anyone needs Matt's solution in 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
    
    0 讨论(0)
  • 2020-11-28 11:24

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

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

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

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

    0 讨论(0)
  • 2020-11-28 11:30

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

    0 讨论(0)
  • 2020-11-28 11:30

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

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

    0 讨论(0)
提交回复
热议问题