Sobes.tech
Middle+

Kaip vyksta darbai su srautais ir užduotimis C# kalboje, kai keli srautai kreipiasi į bendrą būseną?

sobes.tech AI

Atsakymas iš AI

Dirbant su bendru būsena keliuose srautuose C# kyla sinchronizacijos problemų ir galimų lenktynių sąlygų. Norint užtikrinti srautų saugumą, naudojami šie mechanizmai:

  1. Užraktas (Locking):

    • Naudojamas lock operatorius kritinei sekcijai sukurti, garantuojant, kad tik vienas srautas gali vienu metu pasiekti saugomą išteklių.
    • Reikalauja objektinio užrakto objekto (paprastai private readonly object), kuris naudojamas sinchronizacijai.
    // Pavyzdys naudojant lock
    private readonly object _lockObject = new object();
    private int _sharedCounter = 0;
    
    public void IncrementCounter()
    {
        lock (_lockObject)
        {
            _sharedCounter++; // Kritinė sekcija
        }
    }
    
  2. Atomarinės operacijos:

    • System.Threading.Interlocked klasė teikia atomines operacijas (pvz., Increment, Decrement, Add, Exchange), kurios yra srautui saugios ir nereikalauja aiškios užrakto taikymo vienai operacijai.
    // Pavyzdys naudojant Interlocked
    private volatile int _sharedValue = 0; // volatile matomumui
    
    public void UpdateValue(int newValue)
    {
        Interlocked.Exchange(ref _sharedValue, newValue); // Atomarinis priskyrimas
    }
    
    public void IncrementValue()
    {
         Interlocked.Increment(ref _sharedValue); // Atomarinis didinimas
    }
    
    • Nors volatile užtikrina pokyčių matomumą tarp srautų, jis nesuteikia atomariškumo daugiasluoksnių operacijų. Atomariškoms operacijoms naudokite Interlocked.
  3. Sinchronizacijos klasės iš System.Threading ir System.Threading.Tasks:

    • Monitor: žemesnio lygio užrakto mechanizmas, palyginti su lock.
    • Mutex: leidžia sinchronizuoti prieigą prie išteklių tarp kelių procesų.
    • SemaphoreSlim: riboja vienu metu prieinamų srautų skaičių.
    • ReaderWriterLockSlim: leidžia daugeliui srautų vienu metu skaityti išteklius, bet tik vienam rašyti.
  4. Srautui saugios kolekcijos:

    • System.Collections.Concurrent erdvė apima kolekcijas (ConcurrentBag<T>, ConcurrentDictionary<TKey, TValue>, ConcurrentQueue<T>, ConcurrentStack<T>), sukurtas specialiai daugiagretėms prieigoms. Šių kolekcijų naudojimas sumažina poreikį rankiniu būdu sinchronizuoti bazines operacijas.
    // Pavyzdys naudojant 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);
    }
    
  5. Nekeičiami objektai (Immutable objects):

    • Sukurti nekeičiami objektai reiškia, kad jų būsena po sukūrimo negali būti pakeista. Jei bendroji būsena yra nekeičiama objekto reprezentacija, keli srautai gali saugiai ją skaityti be sinchronizacijos. Būsena atnaujinama kuriant naują objekto egzempliorių.

Dirbant su užduotimis (Task) TPL (Task Parallel Library), taip pat būtina atsižvelgti į sinchronizaciją prieigai prie bendros būsenos. TPL palengvina paralelinių operacijų kūrimą ir valdymą, bet nesumažina atsakomybės už duomenų saugumą.

Pasirinkimas tinkamo sinchronizacijos mechanizmo priklauso nuo konkrečios užduoties ir prieigos prie bendros būsenos ypatybių.