Sobes.tech
Middle+

Čo treba urobiť, ak dvaja pracovníci súčasne pristupujú k rovnakým údajom?

sobes.tech AI

Odpoveď od AI

Je potrebné zabezpečiť vlákno-bezpečný prístup k údajom. Hlavné prístupy:

  1. Zámky (Locks): Používajú sa na obmedzenie prístupu ku kritickým sekciám kódu. Len jedno vlákno môže vlastniť zámok v danom momente.

    # Príklad s Mutex v Ruby
    require 'thread'
    
    $shared_data = 0
    $mutex = Mutex.new
    
    def increment_data
      $mutex.lock # Získanie zámku
      begin
        # Kritická sekcia
        temp = $shared_data
        sleep(0.01) # Napodobnenie práce
        $shared_data = temp + 1
      ensure
        $mutex.unlock # Uvoľnenie zámku
      end
    end
    
    threads = []
    10.times do
      threads << Thread.new { increment_data }
    end
    
    threads.each(&:join)
    puts $shared_data # Garantovane bude 10
    
  2. Transakcie databáz: Ak sú údaje uložené v databáze, používajte transakcie pre atomické operácie. Úroveň izolácie transakcií určuje, ako sú viditeľné zmeny vykonané inými transakciami.

    # Príklad v Rails s použitím ActiveRecord
    ActiveRecord::Base.transaction do
      # Všetky operácie v bloku sa vykonávajú atomicky
      user = User.find(user_id)
      user.balance -= amount
      order = Order.create!(user: user, amount: amount)
      user.save!
      # Ak niečo zlyhá, celá transakcia sa vráti
    end
    
  3. Atomické operácie: Niektoré jazyky a knižnice poskytujú atomické typy údajov alebo operácie, ktoré sú garantovane vykonané ako celok, bez možnosti prerušenia inými vláknami.

    # Ruby nemá vstavané atomické typy pre ľubovoľné údaje,
    # ale niektoré operácie (napríklad priradenie) môžu byť atomické
    # pre jednoduché typy. Pre zložitejšie scenáre sú potrebné zámky.
    
  4. Optimistická kontrola súbehu: Namiesto zámkov majú každý záznam číslo revízie alebo časovú značku. Pri aktualizácii sa kontroluje, či sa záznam nezmenil od jeho čítania. Ak sa zmenil, operácia sa reštartuje.

    # Príklad s ActiveRecord::Locking::Optimistic
    # Pridajte `lock_version` (integer) do vašej tabuľky
    class Product < ApplicationRecord
      # ...
    end
    
    product = Product.find(id)
    # Iné vlákno aktualizuje ten istý produkt...
    begin
      product.price += 10
      product.save! # Vygeneruje ActiveRecord::StaleObjectError, ak sa zmenil lock_version
    rescue ActiveRecord::StaleObjectError
      # Spracovanie konfliktu, napríklad, opätovné načítanie a pokus
      retry
    end
    
  5. Posolové sprostredkovatelia / Fronty: Namiesto priameho prístupu k údajom, pracovníci posielajú správy sprostredkovateľovi (napríklad RabbitMQ, Sidekiq s Redisom). Spracovanie správ prebieha sekvenčne alebo v riadenom poradí, čo znižuje pravdepodobnosť konfliktov.

  6. Použitie nemenných objektov: Ak sú údaje nemenné, súčasný prístup k nim je bezpečný, pretože žiadny pracovník ich nemôže zmeniť.

Výber prístupu alebo kombinácie prístupov závisí od typu údajov, požiadaviek na výkon a architektúry aplikácie. Vo webových aplikáciách na Ruby on Rails sa často používajú transakcie ActiveRecord a optimistické zámky pre údaje v databáze, zatiaľ čo pre spoločné zdroje v pamäti - Mutex. Pozadie úlohy (napríklad Sidekiq) zvyčajne spracovávajú správy z front, ktoré samy o sebe zabezpečujú určitú mieru usporiadania.