Non, ce que vous avez n'est pas sûr. La vérification pour voir si _count >= MAXIMUM
pourraient course avec l'appel à l' Interlocked.Increment
partir d'un autre thread. C'est en fait vraiment très dur à résoudre à l'aide de basse-lock techniques. Pour obtenir que cela fonctionne correctement, vous devez avoir à faire une série de plusieurs opérations apparaissent atomique sans l'aide d'un verrou. C'est la partie la plus difficile. La série d'opérations en question ici sont:
- Lire
_count
- Test
_count >= MAXIMUM
- Rendre une décision fondée sur ce qui précède.
- Incrément
_count
selon la décision rendue.
Si vous ne faites pas tous les 4 de ces étapes apparaissent atomique puis il y aura une course à condition. Le modèle standard pour l'exécution d'une opération complexe, sans prendre un verrou est comme suit.
public static T InterlockedOperation<T>(ref T location)
{
T initial, computed;
do
{
initial = location;
computed = op(initial); // where op() represents the operation
}
while (Interlocked.CompareExchange(ref location, computed, initial) != initial);
return computed;
}
Remarquez ce qui se passe. L'opération est exécutée de façon répétée jusqu'à ce que l'ICX opération détermine que la valeur initiale n'a pas changé entre le moment où elle a été la première lecture et le temps de la tentative a été faite pour le changer. C'est le modèle standard et la magie, tout se passe en raison de l' CompareExchange
(ICX) appel. Notez, cependant, que cela ne tienne pas compte de l' ABA problème.1
Ce que vous pourriez faire:
Donc, en prenant le dessus motif et l'incorporer dans votre code entraînerait dans cette.
public void CheckForWork()
{
int initial, computed;
do
{
initial = _count;
computed = initial < MAXIMUM ? initial + 1 : initial;
}
while (Interlocked.CompareExchange(ref _count, computed, initial) != initial);
if (replacement > initial)
{
Task.Run(() => Work());
}
}
Personnellement, je punt sur le bas-lock stratégie entièrement. Il y a plusieurs problèmes avec ce que j'ai présenté ci-dessus.
- Cela pourrait effectivement fonctionner plus lentement que de prendre un hard lock. Les raisons sont difficiles à expliquer et à l'extérieur de la portée de ma réponse.
- Toute dérogation à ce qui est au-dessus va probablement provoquer le code à l'échec. Oui, c'est vraiment fragile.
- C'est difficile à comprendre. Je veux dire regarder. Qu'il est laid.
Ce que vous devez faire:
Aller avec le hard lock route votre code pourrait ressembler à ceci.
private object _lock = new object();
private int _count;
public void CheckForWork()
{
lock (_lock)
{
if (_count >= MAXIMUM) return;
_count++;
}
Task.Run(() => Work());
}
public void CompletedWorkHandler()
{
lock (_lock)
{
_count--;
}
}
Remarquez que c'est beaucoup plus simple et beaucoup moins sujettes à erreur. Vous pouvez effectivement trouver que cette approche (hard lock) est effectivement plus rapide que ce que j'ai montré ci-dessus (bas de verrouillage). Encore une fois, la raison en est délicate et il existe des techniques qui peuvent être utilisés pour accélérer les choses, mais en dehors de la portée de cette réponse.
1L'ABA problème n'est pas vraiment un problème dans ce cas, car la logique ne dépend pas de l' _count
restant inchangée. Il importe seulement que sa valeur est la même à deux points dans le temps, indépendamment de ce qui s'est passé entre les deux. En d'autres termes, le problème peut être réduit à celui dans lequel il semblait comme la valeur n'a pas changé, même si, en réalité, il peut avoir.