🔹 1. Pourquoi ConcurrentDictionary<TKey, TValue> est préféré à Dictionary en multi-thread ?
-
Thread-safe pour lecture et écriture concurrentes.
-
Evite les exceptions et incohérences de données (
InvalidOperationExceptionou corruption de mémoire). -
Optimisé pour opérations atomiques comme
TryAdd,TryUpdate,GetOrAdd. -
Exemple :
var dict = new ConcurrentDictionary<string, double>();
dict.TryAdd("AAPL", 150.5);
dict.AddOrUpdate("AAPL", 150.5, (key, oldValue) => oldValue + 1);
✅ Dans un environnement multi-threadé,
Dictionarynécessite unlockexterne pour garantir la sécurité.
🔹 2. CancellationToken et utilisation dans les tâches longues
-
Permet d’annuler proprement des tâches en cours sans les forcer.
-
Méthode : passer un
CancellationTokenà la tâche et vérifier régulièrementtoken.IsCancellationRequested. -
Exemple :
public async Task ComputeRiskAsync(CancellationToken token)
{
for (int i = 0; i < 1_000_000; i++)
{
token.ThrowIfCancellationRequested();
// calcul lourd
}
}
-
Avantage : réactivité + nettoyage des ressources.
🔹 3. Différence entre lock, Monitor, SemaphoreSlim, Mutex
| Outil | Scope | Usage | Performance / Latence |
|---|---|---|---|
lock(obj) |
Thread local | Simplifie Monitor.Enter/Exit |
Rapide, syntaxe simple |
Monitor |
Thread local | Contrôle plus fin (TryEnter, Wait/Pulse) | Flexibilité, légèrement plus verbeux |
SemaphoreSlim |
Multi-thread | Limite nombre de threads concurrents | Asynchrone-friendly (WaitAsync) |
Mutex |
Cross-process | Synchronisation entre processus | Plus lent, kernel-level |
🔹 4. Tester et éviter les conditions de course (race conditions)
✅ Stratégies :
-
Utiliser des collections thread-safe (
ConcurrentDictionary,ConcurrentQueue) -
Utiliser des locks ou Interlocked pour opérations atomiques
-
Unit tests multi-threadés et stress tests
-
Static code analysis et tools comme Microsoft CHESS
🔹 5. Lazy pour initialisation d’objets lourds
-
Crée l’objet uniquement lorsqu’il est utilisé pour la première fois
-
Thread-safe par défaut (
LazyThreadSafetyMode.ExecutionAndPublication) -
Exemple :
Lazy<RiskEngine> engine = new Lazy<RiskEngine>(() => new RiskEngine());
var result = engine.Value; // initialisation ici
✅ Utile pour les objets de pricing ou risk engine qui consomment beaucoup de mémoire.
🔹 6. Stratégies pour éviter les deadlocks
-
Ordre fixe pour acquérir les locks
-
Utiliser timeout sur Monitor.TryEnter / SemaphoreSlim.Wait
-
Éviter locks imbriqués lorsque possible
-
Privilégier lock-free structures et
ConcurrentDictionary -
Limiter la durée de chaque lock → lock court et ciblé
🔹 7. Utilisation de Task.WhenAll() et Task.WhenAny()
-
Task.WhenAll() : attendre plusieurs tâches parallèles et récupérer les résultats
var tasks = symbols.Select(s => ComputePriceAsync(s));
var results = await Task.WhenAll(tasks);
-
Task.WhenAny() : continuer dès qu’une tâche se termine (utile pour first response wins)
var first = await Task.WhenAny(tasks);
🔹 8. Différence entre async/await et TPL (Task Parallel Library)
| Concept | Description |
|---|---|
| async/await | Syntaxe simplifiée pour programmation asynchrone (IO bound) |
| TPL (Task Parallel Library) | Gestion avancée de parallelisme CPU-bound, Task.Run, Parallel.ForEach |
✅ Exemple :
async/await= streaming de données marché, TPL = calculs massifs sur portefeuilles.
🔹 9. Profiler et optimiser les performances multi-threading
-
Outils :
-
Visual Studio Profiler, JetBrains dotTrace, PerfView
-
Thread contention analysis, GC pauses
-
-
Optimisations :
-
Minimiser locks → préférer collections thread-safe
-
Pré-allocation mémoire → éviter GC dans hot path
-
Batch processing → réduire fréquence context-switch
-
Affinity threads → CPU pinning pour threads critiques
-
🔹 10. Utilisation de SpinLock en latence ultra-faible
-
SpinLock : boucle active au lieu de bloquer le thread
-
Idéal pour section critique très courte
-
Pas adapté aux sections longues → CPU waste
-
Exemple :
SpinLock spin = new SpinLock();
bool lockTaken = false;
try
{
spin.Enter(ref lockTaken);
// code ultra court et critique
}
finally
{
if (lockTaken) spin.Exit();
}
✅ Utilisé en HFT / trading ultra-low latency pour éviter context switch du kernel.
📘 Résumé des bonnes pratiques Multi-threading / Concurrence
| Concept | Astuce / Usage |
|---|---|
| ConcurrentDictionary | Thread-safe, opérations atomiques |
| CancellationToken | Annulation propre, réactivité |
| lock / Monitor / SemaphoreSlim / Mutex | Choisir selon scope, async, cross-process |
| Race conditions | Tests multi-thread, collections thread-safe |
| Lazy | Initialisation différée thread-safe |
| Deadlocks | Locks courts, ordre fixe, timeout |
| Task.WhenAll / Task.WhenAny | Synchroniser tâches parallèles |
| async/await vs TPL | IO-bound vs CPU-bound |
| SpinLock | Sections critiques ultra-courtes, faible latence |
| Profiling | GC, contention, hot path, allocation pools |
Aucun commentaire:
Enregistrer un commentaire