2009-08-25 5 views
6

Disons que j'ai le code suivantLes threads attendent-ils sur un verrou FIFO?

static class ... 
{ 
    static object myobj = new object(); 

    static void mymethod() 
    { 
     lock(myobj) 
     { 
      // my code.... 
     } 
    } 
} 

Alors disons que si thread1 a le verrou thread2 tente d'exécuter MyMethod. Attend-il que le verrou soit libéré ou déclenche une exception?

En cas d'attente, l'ordre est-il assuré de sorte que si des threads supplémentaires entrent, ils sont FIFO?

Répondre

8

Mis à jour ma réponse: Ils sont mis en file d'attente, mais la commande n'est pas garantie être FIFO.

Vérifiez ce lien: http://www.albahari.com/threading/part2.aspx

+4

Pas nécessairement, voir: http://stackoverflow.com/questions/961869/is-there-a-synchronization-class-that-guarantee-fifo-order-in-c/961904 –

-1

Il attendra, et ils ne seront pas dans le même ordre.

En fonction de vos besoins, vous pourriez avoir plus de performances si vous regardez quelque chose comme un ReaderWriterLock ou autre chose que juste lock

+1

Ils ne sont pas les mêmes commande. –

3

Il ne ressort pas de votre code comment peut-myobj arriver à être visible à l'intérieur mymethod. On dirait que var myobj est une variable de pile locale à la portée de la déclaration (depuis est var). Dans ce cas, il se peut que chaque thread ait une instance distincte et que le mymethod ne bloque pas.

Mise à jour

A propos de l'ensemble argument FIFO, quelques informations de fond est nécessaire: le CLR ne fournit pas syncronization. C'est le CLR hôte qui fournit cela comme service à l'exécution CLR. L'hôte implémente IHostSyncManager et d'autres interfaces et fournit les différentes primitives de synchronisation. Cela peut sembler hors de propos car l'hôte le plus courant est l'hôte d'application typique (c'est-à-dire que vous compilez et exe) et cela affecte toute la synchronisation au système d'exploitation (vos anciennes primitives de livre Petzold dans l'API Win32). Cependant, il existe au moins deux autres environnements d'hébergement majeurs: l'ASP.Net (je ne suis pas sûr de ce que cela fait) et SQL Server. Ce que je peux dire avec certitude, c'est que SQL Server fournit toutes les primitives sur le SOS (qui est essentiellement un système d'exploitation plus utilisateur), ne touchant jamais les primitives du système d'exploitation, et les primitives SOS sont injustes pour éviter les convois de verrouillage (ie. garantie sans FIFO). Comme le lien dans l'autre réponse l'a déjà souligné, les primitives du système d'exploitation ont également commencé à fournir un comportement injuste, pour la même raison d'éviter les convois de verrouillage.

Pour plus d'informations sur les convois de verrouillage, vous devriez lire les articles Rick Vicik à Designing Applications for High Performance:

verrouillage Convoi

serrures FIFO garantissent l'équité et enchaînez au détriment des provoquant des convois de verrouillage . Le terme signifiait à l'origine de plusieurs threads exécution de la même partie du code que un groupe résultant dans des collisions plus que si elles ont été distribués au hasard à travers le code (un peu comme automobiles étant groupés en paquets par des feux tricolores).Le phénomène particulier dont je parle est pire parce qu'une fois qu'il forme le transfert implicite de la propriété de verrou conserve les threads en lock-step.

Pour illustrer, considérons l'exemple où un thread détient un verrou et est préempté tout en maintenant le verrou. Le résultat est que tous les autres threads s'accumulent sur la liste d'attente pour ce verrou . Lorsque le thread préempté (verrouiller propriétaire à ce moment) peut exécuter à nouveau et libère le verrou, il automatiquement la propriété du verrou à la première thread sur la liste attendre . Ce thread peut ne pas fonctionner pendant un certain temps, mais l'horloge "hold time" est en cours d'exécution. Le propriétaire précédent demande généralement à nouveau le verrou avant la liste d'attente est vidé, perpétuant le convoi

+0

vous avez raison, c'est un objet statique dans un objet statique, j'ai écrit trop vite. Va le réparer maintenant – Matt

0

Windows et le CLR tenter de leur mieux pour garantir l'équité (l'ordre FIFO) de l'attente. Cependant, il existe certains scénarios où l'ordre des threads en attente sur un verrou peut être modifié, la plupart tournent autour des attentes alertes et tout le verrouillage de thread CLR place le thread dans un état d'alerte.

À toutes fins pratiques, vous pouvez supposer que la commande sera FIFO; Cependant, soyez conscient de ce problème.

1

Un exemple simple nous dire que l'ordre ne soit pas garanti FIFO

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Diagnostics; 


namespace ConsoleApplication 
{ 
    class Program 
    { 
     private static Info info = new Info(); 

     static void Main(string[] args) 
     { 
      Thread[] t1 = new Thread[5]; 
      for (int i = 0; i < 5; i++) 
      { 
       t1[i] = new Thread(info.DoWork); 
      } 

      Thread[] t2 = new Thread[5]; 
      for (int i = 0; i < 5; i++) 
      { 
       t2[i] = new Thread(info.Process); 
      } 

      for (int i = 0; i < 5; i++) 
      { 
       t1[i].Start(); 
       t2[i].Start(); 
      } 

      Console.ReadKey(); 
     } 
    } 

    class Info 
    { 
     public object SynObject = new object(); 

     public void DoWork() 
     { 
      Debug.Print("DoWork Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId); 
      lock (this.SynObject) 
      { 
       Debug.Print("Thread Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId); 
       Thread.Sleep(5000); 
       Debug.Print("Thread Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId); 
      } 
     } 

     public void Process() 
     { 
      Debug.Print("Process Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId); 
      lock (this.SynObject) 
      { 
       Debug.Print("Process Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId); 
       Thread.Sleep(5000); 
       Debug.Print("Process Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId); 
      } 
     } 
    } 
} 
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Diagnostics; 


namespace ConsoleApplication 
{ 
    class Program 
    { 
     private static Info info = new Info(); 

     static void Main(string[] args) 
     { 
      Thread[] t1 = new Thread[5]; 
      for (int i = 0; i < 5; i++) 
      { 
       t1[i] = new Thread(info.DoWork); 
      } 

      Thread[] t2 = new Thread[5]; 
      for (int i = 0; i < 5; i++) 
      { 
       t2[i] = new Thread(info.Process); 
      } 

      for (int i = 0; i < 5; i++) 
      { 
       t1[i].Start(); 
       t2[i].Start(); 
      } 

      Console.ReadKey(); 
     } 
    } 

    class Info 
    { 
     public object SynObject = new object(); 

     public void DoWork() 
     { 
      Debug.Print("DoWork Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId); 
      lock (this.SynObject) 
      { 
       Debug.Print("Thread Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId); 
       Thread.Sleep(5000); 
       Debug.Print("Thread Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId); 
      } 
     } 

     public void Process() 
     { 
      Debug.Print("Process Lock Reached: {0}", Thread.CurrentThread.ManagedThreadId); 
      lock (this.SynObject) 
      { 
       Debug.Print("Process Lock Enter: {0}", Thread.CurrentThread.ManagedThreadId); 
       Thread.Sleep(5000); 
       Debug.Print("Process Lock Exit: {0}", Thread.CurrentThread.ManagedThreadId); 
      } 
     } 
    } 
} 

exécution procéderons quelque chose comme ça

Process Lock Reached: 15 
Process Lock Enter: 15 
DoWork Lock Reached: 12 
Process Lock Reached: 17 
DoWork Lock Reached: 11 
DoWork Lock Reached: 10 
DoWork Lock Reached: 13 
DoWork Lock Reached: 9 
Process Lock Reached: 18 
Process Lock Reached: 14 
Process Lock Reached: 16 
Process Lock Exit: 15 
Thread Lock Enter: 9 
Thread Lock Exit: 9 
Process Lock Enter: 14 
Process Lock Exit: 14 
Thread Lock Enter: 10 
Thread Lock Exit: 10 
Thread Lock Enter: 11 
Thread Lock Exit: 11 
Process Lock Enter: 16 
Process Lock Exit: 16 
Thread Lock Enter: 12 
Thread Lock Exit: 12 
Process Lock Enter: 17 
Process Lock Exit: 17 
Thread Lock Enter: 13 
Thread Lock Exit: 13 
Process Lock Enter: 18 
Process Lock Exit: 18 

Comme ca vous voyez le processus de verrouillage de portée est différente que le verrouillage entrer.

Questions connexes