MVarとTVar

# 2つのVar

2つのVarがある。
それぞれ、(自分の中では!)以下の感じ
MVar : R/Wの順番が決まっている。デッドロック注意。
TVar : R/Wをランダムにできる。中身に注意。

## MVar

空で初期化は、`newEmptyMVar :: IO (MVar a)`
値で初期化は、`newMVar :: a -> IO (MVar a)`
書き込みは、`putMVar :: MVar a -> a -> IO ()`
読み出しは、`takeMVar :: MVar a -> IO a`

MVarの中身が空の場合のみputでき、MVarの中身がある場合のみtakeできる。
それぞれ、動作できる状態でない場合、動作できるまで、wait(?)する。

つまり、
put -> take -> put -> take -> ..の繰り返しのみ
R/Wの順番が決まっている。

読むだけのスレッドと、書くだけのスレッドで、順番に動作することが保障されてる時や、
単一スレッドで、ループ中に値を変化させながら使う時に使える。はず。

こんな感じで使える。

```MVar.hs
import           Control.Concurrent

main :: IO ()
main = do
  mVar <- newEmptyMVar
  forkIO $ putLoop mVar 5
  takeLoop mVar 5
  return ()

putLoop :: MVar Int -> Int -> IO ()
putLoop _ 0 = return ()
putLoop mVar i = do
  putMVar mVar i
  putLoop mVar $ i - 1

takeLoop :: MVar Int -> Int -> IO ()
takeLoop _ 0 = return ()
takeLoop mVar i = do
    a <- takeMVar mVar
    takeLoop mVar $ i - 1
```

ちなみに、上記ソースで、
putLoopのloop数 < takeLoopのloop数
としたり、
putLoopとtakeLoopを入れ替えて、takeLoopのloop数 < putLoopのloop数
として実行すると、

```
MVar.hs: thread blocked indefinitely in an MVar operation
```
というエラーが発生する。
ただし、実行後すぐに発生はせず、可能なところまで動作し、解除されないMVarのwait(?)に入ってからエラーが発生する。
(上記ソースで、putLoopのloop数を5、takeLoopのloop数を6とした場合、
5回分のputMVar,takeMVarまでは、正常に実行され、6回目のtakeMVarでエラーが出る。)

また、
MVarには、
`readMVar :: MVar a -> IO a`
というのもある。
これは、MVarの内容を取得するが、MVarを空にしない。
readMVar は、中で、takeMVarしてからputMVarしてるらしい。
ただし、もちろん、MVarの中身が空の時は、waitする。

上記のputMVar, takeMVar, readMVarのnon-blocking版(=waitしない版)もあり、それぞれ
`tryPutMVar :: MVar a -> IO Bool`
`tryTakeMVar :: MVar a -> IO (Maybe a)`
`tryReadMVar :: MVar a -> IO (Maybe a)`

tryPutMVarは、put出来たかの成否、
tryTakeMVar,tryReadMVarは、取れたらJustの値、取れなければNothing
が返る。

## TVar

TransactionalなVar。
複数スレッドからランダムにR/Wを行う場合に使える。(はず。)

基本的に、STMモナドなので注意。(IOモナドで返す関数もいくつかある。)

空で初期化は無さそう。
値で初期化は、`newTVar :: a -> STM (TVar a)`
書き込みは、`writeTVar :: TVar a -> a -> STM ()`
読み出しは、`readTVar :: TVar a -> STM a`

こんな感じで使える。

```TVar.hs
import           Control.Concurrent
import           Control.Concurrent.STM
import           Control.Monad.IO.Class
import           Control.Monad.STM

main :: IO ()
main = do
  tVar <- atomically $ newTVar 0
  forkIO $ putLoop tVar 5
  takeLoop tVar 5
  return ()

putLoop :: TVar Int -> Int -> IO ()
putLoop _ 0 = return ()
putLoop tVar i = do
  threadDelay (4 * 100 * 1000)
  atomically $ writeTVar tVar i
  print $ "writeTVar(" ++ show i ++ ") : " ++ show i
  putLoop tVar $ i - 1

takeLoop :: TVar Int -> Int -> IO ()
takeLoop _ 0 = return ()
takeLoop tVar i = do
  threadDelay (10 * 100 * 1000)
  a <- atomically $ readTVar tVar
  print $ "readTVar(" ++ show i ++ ") : " ++ show a
  takeLoop tVar $ i - 1
```
これで、出力がこんな感じになる。

![TVar.PNG](https://qiita-image-store.s3.amazonaws.com/0/162097/cc290017-6649-7146-b5f9-20534de53183.png)

また、TVarの中身を取り出して、それを変更する関数も用意されている。
`modifyTVar :: TVar a -> (a -> a) -> STM ()`
(modifyTVarは、non-strict。strict版のmodifyTVar'もある。)

こんな感じで使える。

```TVar_modify.hs
import           Control.Concurrent
import           Control.Concurrent.STM
import           Control.Monad.IO.Class
import           Control.Monad.STM

main :: IO ()
main = do
  tVar <- atomically $ newTVar []
  forkIO $ putLoop tVar 5
  threadDelay (3 * 100 * 1000)
  takeLoop tVar 5
  return ()

putLoop :: TVar [Int] -> Int -> IO ()
putLoop _ 0 = return ()
putLoop tVar i = do
  threadDelay (5 * 100 * 1000)
  atomically $ modifyTVar tVar (\is -> i : is )
  print $ "modifyTVar(" ++ show i ++ ") : Add " ++ show i
  putLoop tVar $ i - 1

takeLoop :: TVar [Int] -> Int -> IO ()
takeLoop _ 0 = return ()
takeLoop tVar i = do
  threadDelay (5 * 100 * 1000)
  a <- atomically $ readTVar tVar
  print $ "readTVar(" ++ show i ++ ") : " ++ show a
  takeLoop tVar $ i - 1
```

出力はこんな感じになる。
![TVar_modify.PNG](https://qiita-image-store.s3.amazonaws.com/0/162097/d774b358-deff-d9da-35c7-afaf3b4c5d38.png)



# 使用時注意
## 中身は遅延評価されるので、注意
下みたいなことをすると、おそらく思ってた値と違う値になるので注意。

```TVar_lazy.hs
import           Control.Concurrent
import           Control.Concurrent.STM
import           Control.Monad.IO.Class
import           Control.Monad.STM
import           System.IO.Unsafe       (unsafePerformIO)

main :: IO ()
main = do
  tVar <- atomically $ newTVar []
  kVar <- atomically $ newTVar 0
  putLoop tVar kVar 5

  k <- atomically $ readTVar kVar
  print $ "kVar : " ++ show k

  t <- atomically $ readTVar tVar
  print $ "tVar : " ++ show t
  return ()

putLoop :: TVar [Int] -> TVar Int -> Int -> IO ()
putLoop _ _ 0 = return ()
putLoop tVar kVar i = do
  atomically $ modifyTVar kVar (\k -> k + 1 )
  atomically $ modifyTVar tVar (\ts -> (unsafePerformIO $ calc kVar) : ts )
  print $ "modifyTVar(" ++ show i ++ ") : Add " ++ show (unsafePerformIO $ calc kVar)
  putLoop tVar kVar $ i - 1

calc :: TVar Int -> IO Int
calc kVar = do
  a <- atomically $ readTVar kVar
  return $ a^2
```

上記ソースは

```
"tVar : [25,16,9,4,1]"
```
が出力されそうだが、実際の出力は、こんな感じになる。
![Tvar_lazy.png](https://qiita-image-store.s3.amazonaws.com/0/162097/93437933-812c-05b6-5159-777492cb97d0.png)


期待の出力にするには、putLoopでtVar入れる時に、下みたいに評価させてから入れればいい。

```
import           Control.Exception

~省略~

putLoop tVar kVar i = do
  atomically $ modifyTVar kVar (\k -> k + 1 )
  t <- evaluate $ unsafePerformIO $ calc kVar
  atomically $ modifyTVar tVar (\ts -> t : ts )
  putLoop tVar kVar $ i - 1
```

(まぁ上に書いたソースだと、`unsafePerformIO $ calc kVar`部分をletで拘束して、
それをmodifyTVarとprintで使用すれば、printの方で評価されるので、わざわざevaluateする必要ないですが...)