是否有一个同步类来保证C#中的FIFO顺序?

它是什么以及如何使用?

我需要它,因为我有一个每秒插入DB的计时器,并且我在计时器处理程序和主线程之间有一个共享资源。 我想保证,如果计时器处理程序在插入中花费超过一秒钟,则应按顺序执行等待的线程。 这是我的计时器处理程序的示例代码

private void InsertBasicVaraibles(object param) { try { DataTablesMutex.WaitOne();//mutex for my shared resources //insert into DB } catch (Exception ex) { //Handle } finally { DataTablesMutex.ReleaseMutex(); } } 

但目前互斥锁并不保证任何订单。 我提出详细的问题后没有答案!

你需要编写自己的类才能做到这一点,我找到了这个例子(粘贴因为它看起来好像网站的域名已经失效):

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

用法示例:

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

来自Google缓存的来源

只是阅读Joe Duffy的“Windows上的并发编程”,听起来你通常会从.NET监视器中获得FIFO行为,但在某些情况下不会发生这种情况。

本书的第273页说:“因为监视器在内部使用内核对象,它们表现出与OS同步机制也表现出的相同的大致FIFO行为(在前一章中描述)。监视器是不公平的,所以如果另一个线程偷偷摸摸并获得在唤醒等待线程尝试获取锁之前的锁定,允许偷偷摸摸的线程获取锁定。“

我无法立即找到“上一章”中引用的部分,但它确实注意到在最近版本的Windows中故意使用锁是不公平的,以提高可扩展性并减少锁定车队

你肯定需要你的锁是FIFO吗? 也许有一种不同的方式来解决这个问题。 我不知道.NET中哪些锁保证是FIFO。

您应该重新设计系统,而不是依赖于线程的执行顺序。 例如,让你的线程进行可能需要超过一秒的数据库调用,让你的线程将它们执行的命令放入数据结构(如队列)(如果有一些东西说“这个应该在另一个之前“)。 然后,在业余时间,排空队列并按正确的顺序一次插入一个db。

任何内置同步对象都没有保证顺序: http : //msdn.microsoft.com/en-us/library/ms684266(VS.85).aspx

如果你想要一个保证订单,你必须自己尝试构建一些东西,注意它并不像听起来那么容易,特别是当多个线程同时(接近)到达同步点时。 在某种程度上,它们将被释放的顺序将始终是“随机的”,因为你无法预测到达点的顺序,它真的重要吗?

实际上答案是好的,但我解决了问题,删除计时器并运行方法(先前的计时器处理程序)到后台线程如下

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

跟进Matthew Brindley的回答。

如果转换代码

 lock (LocalConnection.locker) {...} 

然后你可以做一个IDisposable或做我做的事情:

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

我决定反对IDisposable,因为它会在每次调用时创建一个新的不可见对象。

至于重入问题,我将代码修改为:

 public sealed class QueuedLock { private object innerLock = new object(); private volatile int ticketsCount = 0; private volatile int ticketToRide = 1; ThreadLocal reenter = new ThreadLocal(); 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 ); } }