Cum se gestionează lucrul cu firele și sarcinile în C# atunci când mai multe fire accesează o stare comună?
sobes.tech AI
Răspuns de la AI
Atunci când lucrați cu o stare comună între mai multe fire în C#, pot apărea probleme de sincronizare și condiții de cursă. Pentru a asigura siguranța firelor, se utilizează următoarele mecanisme:
-
Blocare (Locking):
- Operatorul
lockeste utilizat pentru a crea o secțiune critică care garantează că doar un fir poate accesa resursa protejată o dată. - Necesită un obiect de blocare (de obicei
private readonly object) utilizat pentru sincronizare.
// Exemplu de utilizare a lock private readonly object _lockObject = new object(); private int _sharedCounter = 0; public void IncrementCounter() { lock (_lockObject) { _sharedCounter++; // Secțiune critică } } - Operatorul
-
Operații atomice:
- Clasa
System.Threading.Interlockedoferă operații atomice (de exemplu,Increment,Decrement,Add,Exchange) care sunt sigure pentru fire și nu necesită blocare explicită pentru a efectua o operație.
// Exemplu de utilizare a Interlocked private volatile int _sharedValue = 0; // volatile pentru vizibilitatea modificărilor public void UpdateValue(int newValue) { Interlocked.Exchange(ref _sharedValue, newValue); // Atribuire atomică } public void IncrementValue() { Interlocked.Increment(ref _sharedValue); // Creștere atomică }- Deși
volatileasigură vizibilitatea modificărilor între fire, nu garantează atomicitatea operațiunilor multi-pas. Pentru operațiuni atomice, trebuie utilizatInterlocked.
- Clasa
-
Clase de sincronizare din
System.ThreadingșiSystem.Threading.Tasks:Monitor: mecanism de blocare de nivel inferior comparativ culock.Mutex: permite sincronizarea accesului la o resursă între mai multe procese.SemaphoreSlim: limitează numărul de fire care pot accesa simultan o resursă.ReaderWriterLockSlim: permite mai multor fire să citească resursa în același timp, dar doar unuia să scrie.
-
Colecții sigure pentru fire:
- Namespace-ul
System.Collections.Concurrentconține colecții (ConcurrentBag<T>,ConcurrentDictionary<TKey, TValue>,ConcurrentQueue<T>,ConcurrentStack<T>) proiectate special pentru acces concurent. Utilizarea acestor colecții elimină necesitatea sincronizării manuale pentru operațiile de bază cu colecția.
// Exemplu de utilizare a ConcurrentDictionary private ConcurrentDictionary<string, int> _sharedData = new ConcurrentDictionary<string, int>(); public void AddOrUpdateData(string key, int value) { _sharedData.AddOrUpdate(key, value, (k, oldValue) => oldValue + value); } - Namespace-ul
-
Obiecte imutabile:
- Crearea obiectelor imutabile înseamnă că, odată create, starea lor nu poate fi schimbată. Dacă starea comună este reprezentată de un obiect imutabil, mai mulți fire îl pot citi în siguranță fără a necesita sincronizare. Actualizarea stării se reduce la crearea unei noi instanțe a obiectului.
Atunci când lucrați cu sarcini (Task) în TPL (Task Parallel Library), trebuie, de asemenea, să luați în considerare sincronizarea la accesarea stării comune. TPL simplifică crearea și gestionarea operațiilor paralele, dar nu elimină responsabilitatea de a asigura siguranța firelor atunci când lucrați cu date partajate.
Alegerea mecanismului de sincronizare potrivit depinde de sarcina specifică și de caracteristicile accesului la starea comună.