2010-06-13 10 views
13

To tylko hipotetyczny scenariusz ilustrujący moje pytanie. Załóżmy, że istnieją dwa wątki i jeden TVar współdzielony między nimi. W jednym wątku znajduje się blok atomowy, który odczytuje TVAR i trwa 10 sekund. W innym wątku jest blok atomowy, który zmienia co sekundę TVAR. Czy pierwszy blok atomowy zostanie kiedykolwiek ukończony? Na pewno wróci do początku, bo dziennik jest wiecznie niespójny?Problem monografii STM

Odpowiedz

12

Jak powiedzieli inni: teoretycznie nie ma gwarancji postępu. W praktyce nie ma również gwarancji postępów:

import Control.Monad -- not needed, but cleans some things up 
import Control.Monad.STM 
import Control.Concurrent.STM 
import Control.Concurrent 
import GHC.Conc 
import System.IO 

main = do 
    tv <- newTVarIO 0 
    forkIO (f tv) 
    g tv 

f :: TVar Int -> IO() 
f tv = forever $ do 
    atomically $ do 
      n <- readTVar tv 
      writeTVar tv (n + 1) 
      unsafeIOToSTM (threadDelay 100000) 
    putStr "." 
    hFlush stdout 

g :: TVar Int -> IO() 
g tv = forever $ do 
    atomically $ do 
      n <- readTVar tv 
      writeTVar tv (n + 1) 
      unsafeIOToSTM (threadDelay 1000000) 
    putStrLn "Done with long STM" 

Powyższe nigdy nie mówi "Gotowe z długim STM" w moich testach.

Oczywiście, jeśli uważasz, że obliczenia są nadal będzie ważne/istotne wtedy chciałby albo

  1. pozostawić blok atomowy, wykonywać kosztownych obliczeń, wprowadź blok atomowy/potwierdzić założenia są prawidłowe i/zaktualizuj wartość. Potencjalnie niebezpieczny, ale nie bardziej niż większość strategii blokowania.
  2. Wynotuj wyniki w bloku atomowym, aby nadal prawidłowy wynik nie był niczym więcej niż tanim wyszukiwaniem po próbie.
+2

Doskonały przykład. Chciałem przetestować coś takiego, ale nie znałem funkcji 'unsafeIOToSTM'! – Alex

2

Nie, działałoby dobrze. Dokładny sposób interakcji dwóch wątków zależy od logiki ponownej.

Na przykład, powiedzmy, że masz:

ten tv = do 
    n <- readTVar tv 
    when (n < 7) retry 
    writeTVar tv 0 
    -- do something that takes about 10 seconds 

one tv = do 
    modifyTVar tv (+1) 
    -- do something that takes about 1 second 

więc „ten” gwint będzie w stanie ponawiania aż TVAR osiągnie wartość 7, to będzie postępować.

Należy zauważyć, że nie można bezpośrednio kontrolować, jak długo te obliczenia zajmą w monadie STM. Byłby to efekt uboczny, a efekty uboczne nie są dozwolone w obliczeniach STM. Jedynym sposobem komunikowania się ze światem zewnętrznym jest przekazywanie wartości za pośrednictwem pamięci transakcyjnej.

A to oznacza, że ​​jeśli „baton-passing” logika dzięki pamięci transakcyjnej jest prawidłowe, program działa poprawnie niezależnie od dokładnej kwoty czasu jakakolwiek część trzeba. To część gwarancji STM.

+0

Naprawdę myślę o najgorszym przypadku. Zapomnijmy na chwilę o Retrys i pomyślmy o dwóch wątkach i rozważmy wykonanie STM "dziesięć". Czyta TVar i zatwierdza tę wartość w dzienniku. Tymczasem drugi wątek zmienia tę TVar zawsze podczas wykonywania "dziesiątki".Tak więc, po zakończeniu wykonywania "dziesięciu", wartość w prawdziwej TVAR nie jest taka sama jak wartość początkowo odczytana w "dziesięć", zmuszając "dziesięć" do ponownego zainicjowania dziennika i ponownego wykonania za każdym razem. – Alex

+1

Jak powiedział Yitz, to zależy. Tak, możliwe jest skonstruowanie sytuacji, w której transakcja nigdy się nie zakończy. Lub bardziej formalnie, STM nie gwarantuje postępu. –

5

STM zapobiega zakleszczeniu, ale nadal jest podatny na głód. Możliwe jest, że w przypadku patologicznym działanie atomowe 1s zawsze będzie korzystało z zasobów.

Jednak zmiany tego wydarzenia są bardzo rzadkie - nie wierzę, że kiedykolwiek widziałem to w praktyce.

Informacje na temat semantyki można znaleźć w sekcji Composable Memory Transactions, rozdział 6.5 "Postęp". Mechanizm STM w Haskell gwarantuje tylko, że uruchomiona transakcja zostanie pomyślnie zatwierdzona (tzn. Nie wystąpi zakleszczenie), ale w najgorszym przypadku nieskończona transakcja zablokuje inne.

+0

Dzięki za referencję. – Alex

+0

Programy STM niekoniecznie są blokujące. –