8
votes

Exécution du thread-Safe à l'aide de System.threading.Timer et surveiller

Utilisation d'un System.threading.Timer (CODE> Résultats dans les threads étant filé à partir d'un threadpool code>, ce qui signifie que si l'intervalle d'exécution pour la minuterie expire, tandis qu'un thread est toujours en traitement. Par ordre d'une demande précédente, le même rappel sera délégué pour exécuter sur un autre fil. Cela va évidemment causer des problèmes dans la plupart des cas, à moins que le rappel ne soit à nouveau conscient, mais je me demande comment em> pour y aller le meilleur (sens sûr).

Disons que nous avoir ce qui suit: p> xxx pré>

Si l'ensemble du shebang soit verrouillé, comme tel: p> xxx pré>

ou, devrait seulement L'entrée doit être gérée et lancer des «intrus», comme ça: P>

private void OnOneAtATimeTimerElapsed(object state)
{
    if (!RestrictOneAtATime())
    {
        return;
    }

    //get real busy for two seconds or more

    if(!ReleaseOneAtATime())
    {
        //Well, Hell's bells and buckets of blood!
    }       
}

bool OneAtATimeInProgress = false;

private bool RestrictToOneAtATime()
{
    bool result = false;
    if (OneAtATimeLocker.TryEnterWriteLock(0))
    {
        if(!OneAtATimeInProgress)
        {
            OneAtATimeInProgress = true;
            result = true;
        }
        OneAtATimeLocker.ExitWriteLock();
    }
    return result;
}

private bool ReleaseOneAtATime()
{
    bool result = false;
    //there shouldn't be any 'trying' about it...
    if (OneAtATimeLocker.TryEnterWriteLock(0))
    {            
        if(OneAtATimeInProgress)
        {
            OneAtATimeInProgress = false;
            result = true;
        }
        OneAtATimeLocker.ExitWriteLock();
    }
    return result;
}


3 commentaires

Ou vous pouvez arrêter la minuterie lors de l'entrée du gestionnaire d'événements Tick ... et réactivez à la sortie ...


Je veux que la minuterie continue de conserver des raisons d'audit, mais merci pour l'effort.


Vous voulez garder le tic-shirt pour que cela ne puisse pas travailler? C'est une exigence étrange!


3 Réponses :


0
votes

Cela dépend vraiment de si vous devez traiter chaque tick. Si vous souhaitez simplement mettre à jour certaines données périodiquement pour refléter l'état actuel des choses, vous acceptez probablement de jeter les tiques qui se produisent pendant le traitement de la traitée précédente. Si, d'autre part, vous devez traiter traiter chaque coche, alors vous avez un problème car dans ces cas, vous devez généralement traiter chaque tick de manière opportune.

aussi, si votre gestionnaire de tiques prend régulièrement plus de temps que l'intervalle de minuterie, alors votre intervalle de minuterie est trop courte ou que votre gestionnaire de tick doit être optimisé. Et vous courez le risque d'avoir un énorme arriéré de tiques pour traiter, ce qui signifie que quel que soit le statut que vous mise à jour est quelque peu retardé.

Si vous décidez de jeter des ticks qui se chevauchent (c.-à-d. Les appels entrant pendant que le La chèque précédente est traitée), je vous recommanderais d'utiliser une minuterie unique que vous réinitialisez à chaque fois après le traitement. C'est-à-dire que plutôt que de jeter des tiques empêchant de se chevaucher des tiques.

existe une raison particulière que vous utilisez ReaderWriterLockSlim.grenterTritelock plutôt que plutôt que ?

En outre, si vous allez utiliser ReaderWriterLocksLim ou moniteur , vous devez les protéger avec essayer .. .cinalement . C'est-à-dire en utilisant moniteur : xxx


1 commentaires

Merci pour cette information, mais je mentionne dans d'autres commentaires que je dois réellement auditer le fait que cela se produit; Mais dans le même temps, je ne veux pas faire queue les demandes - ils peuvent être jetés, vient de noter. Mais 0 signifie que je m'attends à ce que le verrou soit immédiatement, afin qu'ils ne soient pas mis en file d'attente, peut-être -1 attendra indéfiniment, cependant.



12
votes

Beaucoup de façons de gérer cela. Un moyen simple est de ne pas fabriquer la minuterie périodique, en faire un coup unique en définissant uniquement l'argument duetime . Puis réactivez la minuterie dans le rappel dans un bloc enfin. Qui garantit que le rappel ne peut pas courir simultanément.

Ceci est bien sûr la variable d'intervalle par le temps d'exécution du rappel. Si cela n'est pas souhaitable et que le rappel ne prend que de temps en temps que la période de la minuterie, une simple serrure fera le travail. Une autre stratégie est une autre stratégie est moniteur.frenter et n'abandonnez pas le rappel s'il renvoie false. Aucun de ceux-ci n'est particulièrement supérieur, choisissez ce que vous préférez.


7 commentaires

Plutôt que d'empêcher la minuterie, j'aimerais faire autre chose; nommément les entrées de journal d'écriture basées sur des occurrences. Une des choses que je suis à l'affût de ces exécutions se chevauchent. En utilisant moniteur pour envelopper le code concerné semble la suggestion la plus appropriée, je pense. Merci.


Je ne sais pas ce que tu veux dire, je n'ai jamais mentionné arrêter la minuterie. Utilisation de moniteur.tryenter () vous permet de connecter des chevauchements.


Désolé, je parlais de votre suggestion de minuterie unique.


BTW, y a-t-il des implications de performance notables d'aucune suggestion?


Non, acquérir une serrure n'est pas chère. Deux douzaines de nanosecondes, donnent ou prennent.


Pour un seul coup System.threading.Timer.Timer S est-il toujours nécessaire de verrouiller car chaque invocation pourrait / sera un fil différent?


Pour répondre à ma propre question à la recherche de la source de référence Fire () verrouille toujours quelque chose avant d'appeler le rappel référencesSource.microsoft.com/#mscorlib/system/threading/... . Cependant, ce n'est probablement pas une bonne idée de s'appuyer sur un détail de mise en œuvre.



1
votes

Un riff légèrement différent sur le même concept, si vous n'avez pas à traiter chaque tick. Ceci est simplement des buts sur la chèque de la minuterie actuelle si vous traitez toujours une tick précédente.

private int reentrancyCount = 0;
...
OnTick()
{
    try
    {
        if (Interlocked.Increment(ref reentrancyCount) > 1)
            return;

        // Do stuff
    }
    finally
    {
        Interlocked.Decrement(ref reentrancyCount);
    }
}


0 commentaires