AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79591505
Accepted
Enlico
Enlico
Asked: 2025-04-25 05:49:56 +0800 CST2025-04-25 05:49:56 +0800 CST 2025-04-25 05:49:56 +0800 CST

No contexto do STM, como é conceitualmente um log de transações e como ele evolui quando a transação é bem-sucedida após algumas tentativas?

  • 772

Por exemplo, considere esta função, que poderia ser usada em um WM para permitir mover uma janela de uma área de trabalho para outra em uma determinada tela,

moveWindowSTM :: Display -> Window -> Desktop -> Desktop -> STM ()
moveWindowSTM disp win a b = do
  wa <- readTVar ma
  wb <- readTVar mb
  writeTVar ma (Set.delete win wa)
  writeTVar mb (Set.insert win wb)
 where
  ma = disp ! a
  mb = disp ! b

e obviamente seu IOinvólucro,

moveWindow :: Display -> Window -> Desktop -> Desktop -> IO ()
moveWindow disp win a b = atomically $ moveWindowSTM disp win a b

e então assumir que

  • nossa transação é bem-sucedida na terceira tentativa,
  • porque na primeira tentativa ele é invalidado por outra transação simultânea sendo confirmada que altera o valor interno malogo após o término wa <- readTVar mada nossa transação,
  • e na segunda tentativa ele é invalidado por outra transação simultânea que altera o conteúdo de uma ou ambas as transações mae mblogo depois writeTVar ma (Set.delete win wa)da nossa transação.

Como o log de transações evoluiria nesse caso e em que ponto a validação falharia?

O trecho relevante de Parallel and Concurrent Programming in Haskell de Simon Marlow está abaixo (mas informações semelhantes estão disponíveis no artigo Beautiful concurrency de Simon Peyton Jones):

Uma transação STM funciona acumulando um log de readTVaroperações writeTVarque ocorreram até o momento durante a transação. O log é usado de três maneiras:

  • Ao armazenar writeTVaras operações no log em vez de aplicá-las imediatamente à memória principal, descartar os efeitos de uma transação é fácil; simplesmente descartamos o log. Portanto, abortar uma transação tem um custo fixo pequeno.

  • Cada um readTVardeve percorrer o log para verificar se o TVarfoi escrito por um writeTVar. Portanto, readTVaré uma operação O(n) no comprimento do log.

  • Como o log contém um registro de todas as readTVaroperações, ele pode ser usado para descobrir o conjunto completo de TVarsleituras durante a transação, o que precisamos saber para implementar retry.

Quando uma transação chega ao fim, a implementação do STM compara o log com o conteúdo da memória. Se o conteúdo atual da memória corresponder aos valores lidos por readTVar, os efeitos da transação são confirmados na memória; caso contrário, o log é descartado e a transação é executada novamente desde o início. Esse processo ocorre atomicamente, bloqueando todos os TVarsenvolvidos na transação durante sua duração. A implementação do STM no GHC não utiliza bloqueios globais; apenas os TVarsenvolvidos na transação são bloqueados durante o commit, de modo que transações que operam em conjuntos disjuntos de TVarspodem prosseguir sem interferência.

haskell
  • 2 2 respostas
  • 83 Views

2 respostas

  • Voted
  1. Li-yao Xia
    2025-04-25T14:57:23+08:002025-04-25T14:57:23+08:00

    Uma transação STM é executada completamente, registrando cada evento em um log, e a consistência é verificada no final para decidir se a transação deve ser confirmada ou não.

    Então, para o seu exemplo de transação

    do
      wa <- readTVar ma
      wb <- readTVar mb
      writeTVar ma (Set.delete win wa)
      writeTVar mb (Set.insert win wb)
    

    Cada tentativa é executada uma readTVarúnica writeTVarvez. Essas funções gravam em um log, que registra se cada evento é uma leitura ou gravação, o TVar afetado e o valor lido ou gravado.

    READ ma a   -- value a is read from ma (note that values are just pointers as far as STM is concerned)
    READ mb b   -- value b is read from mb
    WRITE ma c  -- value c is written to ma
    WRITE mb d  -- value d is written to mb
    

    Durante essa transação, outras threads podem confirmar transações que gravam nos mesmos TVars. A transação atual continua, independentemente disso.

    Quando atomicallytermina de executar uma transação, ele bloqueia todos os TVars que aparecem em seu log para garantir que o que se segue aconteça atomicamente:

    • atomicallyverifica se os valores lidos no log correspondem aos valores atuais nos TVars, o que significa que eles não foram substituídos (ou outra transação escreveu o mesmo valor no TVar, o que é bom).

    • Se o log corresponder aos TVars, os eventos de gravação serão aplicados aos respectivos TVars. É isso que significa "commit" a transação. Os bloqueios são liberados e atomicallyretornados.

    • Caso contrário, há uma incompatibilidade entre o log e o conteúdo de um TVar; o log é descartado, os bloqueios são liberados e atomicallyreinicia do início.

    No seu exemplo, se houve três tentativas, significa que foram criados três logs completos semelhantes aos acima. A única diferença entre os dois primeiros é que, ao atomicallyanalisar esses logs novamente, não foi possível fingir que eles ocorreram atomicamente, então a transação foi reiniciada.

    • 2
  2. Best Answer
    Daniel Wagner
    2025-04-26T03:18:17+08:002025-04-26T03:18:17+08:00

    Acredito que algo assim serviria como um modelo mental bastante decente:

    data Generational a = Generational
        { generation :: Int
        , value :: a
        }
    instance Eq (Generational a) where (==) = (==) `on` generation
    
    -- choose a generation number that's never been chosen before
    -- e.g. by starting at minBound and incrementing one at each call
    freshGeneration :: IO Int
    
    newtype TVar a = TVar (IORef (Generational a))
    
    unsafeReadTVar :: MonadIO m => TVar a -> m (Generational a)
    unsafeReadTVar (TVar ref) = liftIO $ readIORef ref
    
    unsafeWriteTVar :: Int -> TVar a -> Identity a -> IO ()
    unsafeWriteTVar g (TVar ref) (Identity a) = writeIORef ref (Generational g a)
    
    lock, unlock :: TVar a -> IO ()
    
    -- like Map, but with an extra type argument
    data Map1 (key :: k -> *) (value :: k -> *)
    
    data STMLog = STMLog
        { reads :: Map1 TVar Generational
        , writes :: Map1 TVar Identity
        }
    
    emptyLog :: STMLog
    emptyLog = STMLog empty1 empty1
    
    -- throw away the generation
    readsId :: STMLog -> Map1 TVar Identity
    readsId = fmap1 (Identity . value) . reads
    
    newtype STM a = STM (StateT STMLog IO a)
        deriving (Functor, Applicative, Monad)
    
    readTVar :: TVar a -> STM a
    readTVar v = STM do
       log <- get
       case lookup1 v (writes log) <|> lookup1 v (readsId log) of
            Just (Identity a) -> pure a
            Nothing -> do
                ga <- unsafeReadTVar v
                put log { reads = insert1 v ga (reads log) }
                pure (value ga)
    
    writeTVar :: TVar a -> a -> STM ()
    writeTVar v a = STM $ modify \log ->
        log { writes = insert1 v (Identity a) (writes log) }
    
    atomically :: STM a -> IO a
    atomically (STM act) = do
        (a, log) <- runStateT act emptyLog
        let allVars = keys1 $ union1 (readsId log) (writes log)
        traverse1_ lock allVars
        reads' <- traverseWithKey1 (\v _ -> unsafeReadTVar v) (reads log)
        -- N.B. (==) only compares generations here
        -- (the real implementation probably uses some form
        -- of pointer equality instead of storing generations)
        if reads log == reads'
            then do
                g <- freshGeneration
                traverseWithKey1_ (unsafeWriteTVar g) (writes log)
                traverse1_ unlock allVars
                pure a
            else do
                traverse1_ unlock allVars
                atomically (STM act) -- try again
    

    A coisa real é provavelmente um pouco mais complicada/otimizada/robusta. Não vou me preocupar em lidar com exceções aqui, estou assumindo que bloquear uma coleção de variáveis ​​de uma só vez é fácil, não estou mostrando a implementação de retry, não pensei se alguma cerca de memória é necessária, etc.

    • 2

relate perguntas

  • Existe uma classe que modela "patches"?

  • O Estado é uma flecha?

  • Escrevendo uma instância de Functor para um sinônimo de tipo e sendo confundido

  • Módulo Haskell semelhante ao JavaScript (leva em conta o login e funciona para não inteiros)

  • Como o scanl em Haskell funciona na lista de ambos - comparação de dois casos

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve