68 votes

Est-ce que la garantie lock () est acquise dans l’ordre demandé?

Lorsque plusieurs threads demande un verrou sur le même objet, ne le CLR garantir que les verrous seront acquises dans l'ordre où ils ont été demandés?

J'ai écrit un test pour voir si c'était vrai, et il semble indiquer que oui, mais je ne sais pas si c'est définitif.

class LockSequence
{
    private static readonly object _lock = new object();

    private static DateTime _dueTime;

    public static void Test()
    {
        var states = new List<State>();

        _dueTime = DateTime.Now.AddSeconds(5);

        for (int i = 0; i < 10; i++)
        {
            var state = new State {Index = i};
            ThreadPool.QueueUserWorkItem(Go, state);
            states.Add(state);
            Thread.Sleep(100);
        }

        states.ForEach(s => s.Sync.WaitOne());
        states.ForEach(s => s.Sync.Close());
    }

    private static void Go(object state)
    {
        var s = (State) state;

        Console.WriteLine("Go entered: " + s.Index);

        lock (_lock)
        {
            Console.WriteLine("{0,2} got lock", s.Index);
            if (_dueTime > DateTime.Now)
            {
                var time = _dueTime - DateTime.Now;
                Console.WriteLine("{0,2} sleeping for {1} ticks", s.Index, time.Ticks);
                Thread.Sleep(time);
            }
            Console.WriteLine("{0,2} exiting lock", s.Index);
        }

        s.Sync.Set();
    }

    private class State
    {
        public int Index;
        public readonly ManualResetEvent Sync = new ManualResetEvent(false);
    }
}

Impressions:

Allez entrée: 0

0 obtenu de verrouillage

0 de couchage pour 49979998 tiques

Aller entrées: 1

Allez entrée: 2

Allez entrée: 3

Allez entrée: 4

Allez entrée: 5

Allez entrée: 6

Allez entrée: 7

Allez entrée: 8

Allez entrée: 9

0 la sortie de verrouillage

1 obtenu de verrouillage

1 couchage pour 5001 tiques

1 sortie de verrouillage

2 obtenu de verrouillage

2 couchage pour 5001 tiques

2 sortie de verrouillage

3 obtenu de verrouillage

3 de couchage pour 5001 tiques

3 sortie de verrouillage

4 obtenu de verrouillage

4 couchage pour 5001 tiques

4 sortie de verrouillage

5 obtenu de verrouillage

5 couchage pour 5001 tiques

5 sortie de verrouillage

6 obtenu de verrouillage

6 sortie de verrouillage

7 obtenu de verrouillage

7 sortie de verrouillage

8 obtenu de verrouillage

8 sortie de verrouillage

9 obtenu de verrouillage

9 la sortie de verrouillage

79voto

Jon Skeet Points 692016

IIRC, il est fortement probable d'être dans l'ordre, mais il n'est pas garanti. Je crois qu'il y a au moins théoriquement les cas où un thread va être réveillé par erreur, notez qu'il n'est pas toujours le cadenas, et aller à l'arrière de la file d'attente. Il est possible que ce soit uniquement pour Wait/Notify, mais j'ai l'intuition que c'est pour de verrouillage ainsi.

Je certainement ne voudrais pas compter sur elle si vous avez besoin que les choses se produire dans un ordre, construire une Queue<T> ou quelque chose de similaire.

EDIT: je viens de trouver ceci dans Joe Duffy Programmation Simultanée sur Windows qui en gros s'engage à:

Parce que les écrans utilisent les objets du noyau interne, elles présentent le même grossièrement, FIFO comportement que la synchronisation de système d'exploitation également des mécanismes de l'exposition (décrit dans le chapitre précédent). Les moniteurs sont injustes, donc si un autre thread tente d'acquérir le verrou avant éveillé attente thread tente d'acquérir le verrou, le sournois fil est autorisée à acquérir un verrou.

Le "à peu près-FIFO" bits est ce que je pensais avant, et le "sournois fil" bit est une preuve de plus que vous ne devriez pas faire des hypothèses sur FIFO de la commande.

11voto

Michael Burr Points 181287

L' lock déclaration est documenté à utiliser l' Monitor de la classe à mettre en œuvre, c'est le comportement, et les docs pour le Moniteur de classe ne font pas mention (que je peux trouver) de l'équité. Donc, vous ne devriez pas compter sur demande verrous acquis dans l'ordre de demande.

En fait, un article de Jeffrey Richter indique en fait lock n'est pas juste:

Acquis - c'est un vieil article, donc les choses ont peut-être changé, mais étant donné qu'aucune des promesses sont faites dans le contrat pour l' Monitor classe au sujet de l'équité, vous devez supposer le pire.

2voto

Dan Bryant Points 19021

Légèrement tangentielle à la question, mais de pool de threads n'est même pas garanti qu'il va exécuter le travail en file d'attente les éléments dans l'ordre de leur ajout. Si vous avez besoin d'une exécution séquentielle des tâches asynchrones, l'une des options à l'aide de Tâches TPL (également reporté sur la .NET 3.5 via Réactif Extensions). Il ressemblerait à quelque chose comme ceci:

    public static void Test()
    {
        var states = new List<State>();

        _dueTime = DateTime.Now.AddSeconds(5);

        var initialState = new State() { Index = 0 };
        var initialTask = new Task(Go, initialState);
        Task priorTask = initialTask;

        for (int i = 1; i < 10; i++)
        {
            var state = new State { Index = i };
            priorTask = priorTask.ContinueWith(t => Go(state));

            states.Add(state);
            Thread.Sleep(100);
        }
        Task finalTask = priorTask;

        initialTask.Start();
        finalTask.Wait();
    }

Cela a quelques avantages:

  1. L'ordre d'exécution est garantie.

  2. Vous n'avez plus besoin explicite de verrouillage (TPL prend soin de ces détails).

  3. Vous n'avez plus besoin événements et n'ont plus besoin d'attendre de tous les évènements. Vous pouvez simplement dire: attendre la dernière tâche à accomplir.

  4. Si une exception a été jeté dans les tâches, les tâches à venir serait annulée et l'exception serait renvoyé par l'appel à Attendre. Cela peut ou peut ne pas correspondre à votre comportement souhaité, mais est généralement le meilleur comportement pour séquentielle, tâches dépendantes.

  5. À l'aide de la TPL, vous avez plus de flexibilité pour une extension future, tels que l'annulation de soutien, en attente sur les tâches parallèles pour la suite, etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X