2009-06-07 10 views
12

Bu nedir ve nasıl kullanılır?C# içinde FIFO siparişini garanti eden bir senkronizasyon sınıfı var mı?

Her saniye DB'ye ekleyen bir zamanlayıcım olduğu için ve zamanlayıcı işleyici ile ana iş parçacığı arasında paylaşılan bir kaynağa sahip olmak zorundayım. Zamanlayıcı işleyicisinin, ekleme işleminde bir saniyeden uzun sürmesi durumunda, beklemedeki iş parçacıklarının sırayla çalıştırılması gerektiğini garanti ediyorum. Bu benim zamanlayıcı işleyici

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

için bir örnek kod Ama şu anda muteksin herhangi bir sipariş garanti etmez. Detaylı soruya cevap verdikten sonra cevap yok!

+0

tanımı gereği bir kilit FIFO geçerli:

Evreselliğin konu ile ilgili olarak bu kod güncellenmiştir. Bir iş parçacığı içeri girer ve dışarı çıkana kadar başka kimse giremez! –

+10

@Mitch: Ama beklemek zorunda kaldığı ilk iş parçacığının, hemen sonra engellemeyi kaldırdığının garantisi yok. Yani hayır, kilitler FIFO değil. –

+2

(yani, T1 şu anda kilidi tutuyorsa, T2 bunu elde etmeye çalışır, sonra T3 bunu elde etmeye çalışır, T3'ün T2'yi almadan önce alacağı garantisi yoktur.) –

cevap

26

Bunu yapmak için kendi sınıf yazmak gerekir, ben bu örneği bulundu: kullanım

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

Örnek:

QueuedLock queuedLock = new QueuedLock(); 

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

Source via Google cache

+4

QueuedLock'un tek kullanımlık olması durumunda bu daha hoş olurdu. –

+0

Artık günler, ref by by ref, C# derleyicisi tarafından desteklenmiyor. uçucu olarak tedavi edilir. Bunun dışında benim bugünkü problemim için mükemmel bir çözüm! – AlexanderVX

+0

Mükemmel çalışır! – merger

1

üzerinde garantili bir düzen yoktur herhangi yerleşik senkronizasyon nesneleri: http://msdn.microsoft.com/en-us/library/ms684266(VS.85).aspx

bunu gelebilir olduğunca kolay olmadığını gerçi notu, denemek ve şeyi bizzat inşa etmek gerekecek garantili siparişi istiyorsanız Özellikle de birden çok iş parçacığı aynı anda (yakın) eşitleme noktasına ulaştığında. Bir dereceye kadar serbest bırakılacakları emir her zaman 'rastgele' olacaktır, çünkü hangi noktaya ulaşıldığını tahmin edemezsiniz, bu gerçekten önemli mi?

+0

Bağlantı elbette OS ilkelleriyle ilgili ... bir noktada .NET'liler bunlara dayanacak, bu yüzden FIFO'nun garanti edilemeyeceğini ima ediyorum ... ayrıca Jon'un cevabı da. – jerryjvl

+1

Sorun, genellikle kilitleri aynı anda yakalamayı denediğinde rastgele bir sırayla giren iş parçacıklarıyla birlikte değil. Kilitlemeye uzun bir süre girmeyi bekleyen iş parçacığı, kilitlemeye daha sonra girmeye çalışan iş parçacıkları tarafından kesildiğinde sorun ortaya çıkar. 1000 isteklerin sürekli olarak paylaşılan bir kaynağa erişmeye çalıştığı ve bir kilitte beklemedeki ilk iş parçacığının beklemenin önündeki kilidi çalmaya devam ettiği için beklemeyi sürdürdüğü yoğun bir web sunucusu düşünün. –

10

Sadece Joe Duffy'nin "Eşzamanlı Programlama Windows'taki" bölümünü okumak, genellikle .NET monitörlerinden FIFO davranışı elde edersiniz, ancak bunun olmayacağı bazı durumlar vardır.

Kitabın sayfa 273'ü şöyle diyor: "Monitörler çekirdek nesnelerini dahili olarak kullandığından, OS senkronizasyon mekanizmalarının da aynı kabaca FIFO davranışını sergilediklerini gösteriyorlar (önceki bölümde açıklanmaktadır). Monitörler haksız, yani başka bir iş parçacığıysa uyanmış bir bekleme ipliği kilidi almayı denemeden önce gizlice içeri girer ve kilidi alır, sinsi dişin kilidi almasına izin verilir. "

hemen "önceki bölümde" başvurulan bölümünü bulamıyorum ama kilitler ölçeklenebilirlik artırmak ve kilit konvoyları azaltmak için Windows'un son sürümlerinde kasten haksız yapılmıştır notu yok.

FIFO olmak için kilidinize kesinlikle ihtiyacınız var mı? Belki de probleme yaklaşmanın farklı bir yolu vardır. NET'te FIFO olması garantili kilitler bilmiyorum.

(sitenin alan adının geçmesinden sanki çünkü görünüşe yapıştırılan)
+0

Jon Skeet her zamanki gibi, doğru - Win2k3 SP2 (ish?) Ve yukarı% 100 adil kilitleme yok, ve bu kasıtlı. –

+4

-1: gerçek bir çözüm sağlanmadı – Svisstack

+0

Gerçekten eğlenceli olan şey, sinsi parçacığın aynı zamanda Monitör'ü serbest bıraktığı zamandır. Ve böylece, sonsuza kadar bir döngü içinde devam eder, diğer iş parçacığı açlıktan. Bu davranışa sadece CE 5. 0'daki Compact Compact Framework'deki eski bir kodla rastladım. – reirab

7

Sen parçacığı yürütme düzenine dayanmayan sisteminizi yeniden tasarlamak gerekir. Örneğin, iş parçacığınızın bir saniyeden uzun sürecek bir DB çağrısı yapmasını sağlamak yerine, iş parçacığınızın çalıştırdıkları komutu bir sıra gibi bir veri yapısına (ya da bir şey varsa bu bir şey varsa bir yığına) yerleştirmesini sağlayın. Başka bir ") önce. Ardından, boş zamanlarında kuyruğu boşaltın ve db girişlerinizi uygun sırayla birer birer yapın.

1

Aslında cevaplar iyi, ama

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

güzel uyku mantığı! şimdi tüm kodlarımı yeniden yazmak :) – divinci

0

Matthew Brindley cevabı sonrasında takip aşağıdaki gibi ben arka plan iş parçacığı içine zamanlayıcı kaldırarak sorunu çözdü ve (daha önce zamanlayıcı-işleyici) yöntemi çalıştırın.

lock (LocalConnection.locker) {...} 

kod dönüştürme o zaman ya bir IDisposable yapmak ya yapabilirdi

ise ne yaptım: Her çağrı üzerine yeni bir görünmez nesne oluşturur çünkü ben IDisposal karşı karar

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

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

.

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); 
    } 
} 
İlgili konular