2017-10-17 5 views
2

Comment la méthode Monitor.Wait() est-elle implémentée dans la classe system.threading.monitor de C#?C#: Comment Monitor.Wait est-il implémenté?

https://www.codeproject.com/Articles/28785/Thread-synchronization-Wait-and-Pulse-demystified

Conceptuellement, j'envisager quelque chose comme ceci:

class Monitor { 
     public static Wait(object o) 
     { 
      // Release Lock 
      Monitor.Exit(o); 

      // Spinlock until another Thread acquires Lock 
      while(!Monitor.isEnter(o)); 

      // Wait to re-acquire lock 
      Monitor.Enter(o); 
     } 
    } 

Il ce exact? Ou est-ce qu'il manque quelque chose?

Voici l'exemple typique de la classe Monitor que je considère comme une base pour le code précédent.

using System.Threading; 

readonly object o = new object(); 

// In Thread #1: (Where appropriate) 
lock(o) { 
    Monitor.Wait(o); 
} 

//In Thread #2: (Where appropriate) 
lock(o) { 
    Monitor.Pulse(o); 
} 

Et Lock (o) est bien sûr intégré raccourci C# pour:

try { 
    Monitor.Enter(o); 
    { 
     //Lock Block Here 
    } 
} 
finally { 
    Monitor.Exit(o); 
} 
+5

[System.Threading.Monitor] (http://referencesource.microsoft.com/#mscorlib/system/threading/monitor.cs) – Tigran

+0

L'article associé à votre question répond efficacement à ce que je peux dire. Quelles informations supplémentaires recherchez-vous? – DiskJunky

+0

Afin de mieux comprendre la fonction d'attente, j'étais curieux de savoir s'il est possible de le ré-implémenter en tant que fonction simple en utilisant ce qui est déjà donné comme une API .Net publiée. C'est plus facile à comprendre que de code ... –

Répondre

4

Pour voir truely s'il y a un moyen plus simple à mettre en œuvre Monitor.Wait, nous avions AHVE d'enquêter sur la façon dont il fonctionne à un niveau bas. L'implémentation réelle est finalement écrite en C et cachée chez nous mais spécifiquement pour Monitor.Wait(object), nous pouvons tracer la chaîne d'appels de la manière suivante; De là, il est plus difficile de voir ce qui se passe même dans ILSpy. Selon le lien de Tigran à la source de l'objet Monitor, il nous reste ce qui suit dans la source;

/*======================================================================== 
** Waits for notification from the object (via a Pulse/PulseAll). 
** timeout indicates how long to wait before the method returns. 
** This method acquires the monitor waithandle for the object 
** If this thread holds the monitor lock for the object, it releases it. 
** On exit from the method, it obtains the monitor lock back. 
** If exitContext is true then the synchronization domain for the context 
** (if in a synchronized context) is exited before the wait and reacquired 
** 
    ** Exceptions: ArgumentNullException if object is null. 
========================================================================*/ 
    [System.Security.SecurityCritical] // auto-generated 
    [ResourceExposure(ResourceScope.None)] 
    [MethodImplAttribute(MethodImplOptions.InternalCall)] 
    private static extern bool ObjWait(bool exitContext, int millisecondsTimeout, Object obj) 

La description est laissée assez auto-explanitory dans ce qui est qu'il fait et dans quel ordre. Cependant, la mise en œuvre précise par laquelle il le fait est enveloppée de diverses méthodes qui contiennent le code crucial.

externspecifies que l'implémentation réelle réside dans un autre assembly. Il peut être utilisé avec DllImport lors de l'accès au code non géré (pas le cas ici) ou peut être un extern alias. D'ici, comme SO poster asking about where to find the implementation of extern methods, vous auriez à regarder le code C lui-même qui peut être trouvé dans le Core CLR (crédit Scott Chamberlain). De là, nous examinons l'implémentation de la méthode C pour ObjWait() qui maps (ligne 1027) à ObjectNative::WaitTimeout dans le CLR;

FCIMPL3(FC_BOOL_RET, ObjectNative::WaitTimeout, CLR_BOOL exitContext, INT32 Timeout, Object* pThisUNSAFE) 
{ 
    FCALL_CONTRACT; 

    BOOL retVal = FALSE; 
    OBJECTREF pThis = (OBJECTREF) pThisUNSAFE; 
    HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); 

    if (pThis == NULL) 
     COMPlusThrow(kNullReferenceException, W("NullReference_This")); 

    if ((Timeout < 0) && (Timeout != INFINITE_TIMEOUT)) 
     COMPlusThrowArgumentOutOfRange(W("millisecondsTimeout"), W("ArgumentOutOfRange_NeedNonNegNum")); 

    retVal = pThis->Wait(Timeout, exitContext); 

    HELPER_METHOD_FRAME_END(); 
    FC_RETURN_BOOL(retVal); 
} 
FCIMPLEND 

Avant d'entrer dans cela, il vaut la peine de regarder this (crédit aussi Scott Chamberlain), qui stipule; Les appels sont identifiés dans le code managé en tant que méthodes extern avec le jeu de bits MethodImplOptions.InternalCall. Cela explique notre lien vers ObjWait() et ObjectNative::WaitTimeout. Donc, en décomposant plus loin, nous pouvons voir null de base et des vérifications d'arguments avec des exceptions appropriées soulevées si c'est le cas. Le crux est l'appel à pThis->Wait() ... à quel point je ne peux pas tout à fait tracer plus loin ... encore.

De là, nous arrivons à Object::Wait (line 531), puis aller à SyncBlock::Wait (line 3442).À ce stade, nous avons l'essentiel de la mise en œuvre et il y en a beaucoup.

Compte tenu de tout ce qui précède et de revenir à ce que vous avez demandé une implémentation plus simple, je serais prudent de simplifier Monitor.Wait(). Il y a un lot qui se passe sous le capot et il serait très facile de faire une erreur et d'avoir des bugs potentiels dans une implémentation alternative.

EDIT

cri grave vers Scott Chamberlain qui a fait la majeure partie de l'enquête ci-dessous ILSpy niveau et Fouille/débogage la pile de code C. À peu près tout le travail d'enquête au-dessous du niveau d'ILSpy est le sien, je l'ai simplement compilé ici dans une réponse.

+2

https://github.com/dotnet/coreclr/ blob/e0486761d6c019cb696dcd35aeecc031b1d01eef/src/classlibnative/bcltype/objectnative.cpp –

+2

@DiskJunky J'ai commencé à poster la même chaîne que celle que vous avez trouvée, mais j'ai tapé un mur sur le 'pThis-> Wait (Timeout, exitContext);' call dans cette méthode. –

+0

@DStanley ouais, je viens de frapper moi-même. Je vais mettre à jour ma réponse avec les résultats à ce jour pendant que j'étudie plus loin – DiskJunky