2025年8月10日日曜日

Haskell入門P286

以下のコードの目的は、子スレッドのエラーがメインスレッドに影響しないようにしたいため、隔離しているようです。
actionIO ::  IO a -> IO a
actionIO  action = do
 mv <- newEmptyMVar :: IO (MVar (Either SomeException a))
 _tid <- forkIO $ do
  result <- try acttion
  putMVar mv result
 result <- takeMVar mv
 case result of
  Left e -> throwIO e
  Right r -> return 

1. actionIO :: IO a -> IO a
IO aのアクションを引数にとり、結果もIO aとして返す関数です。引数のactionを別スレッドで実行し、その結果を取得します。

2. mv <- newEmptyMVar :: IO (MVar (Either SomeException a))
新しい空のMVarを作成します。MVarはスレッド間で値のやり取りができる同期変数です。
型は MVar (Either SomeException a) としています。Either SomeException aは、「例外が起きたか」あるいは「正常に値が得られたか」を表すために使います。

3. _tid <- forkIO $ do ...
forkIOで新しい軽量スレッドを作り、その中でactionを実行します。
forkIOはスレッドIDを返しますが、このコードでは使わないので_tidとして無視しています。

4. result <- try action
tryは、例外が発生する可能性があるIOアクションを安全に実行し、成功時はRight a 例外発生時はLeft SomeExceptionの形で結果を返します。

5. putMVar mv result
mv(空のMVar)に結果を格納します。これでメインスレッドはこの結果を待つことができます。

6. result <- takeMVar mv
メインスレッドで、mvに格納されるまで待ちます。forkIOの中の処理が終わりputMVarが呼ばれるまでブロックされます。

2025年8月9日土曜日

Haskell入門 P285 MVarによるスレッド間の通信

 ChatGPTもlevel5になって、さらにバージョンアップした感じです。

本を読んでも、なかなか理解しにくかったところも、ChatGPTにコードを解説してもらうとすぐに理解できたので、質問者のレベルに合わせて説明できる能力に驚いています。


・m <- newEmptyMVar newEmptyMVarで空のMVarを作成  これはスレッド間の通知用の「信号」として使う予定
 ・forkIOで新しい軽量スレッドを作成。
  この新しいスレッドの中で、myThreadIdでスレッドIDを取得し、コンソールに表示。
  ・putMVar m ()で空のMVarに通知(単なる空のタプル () を入れているだけ)を送る。
  ・メインスレッドはtakeMVar mで、MVarに値が入るのを待つ(ブロックされる)。
  子スレッドが2秒後にputMVarで通知すると、この待機が解除される。       
  ・メインスレッドは、子スレッドが終わったことをMVar経由で待機している。
   子スレッドは、重たい処理の後にMVarに通知し、メインスレッドに処理完了を知らせる。

 ※ putMVar m () は、普通は MVar aにaを入れる(例えばMVar IntにIntを入れるように)。今は()を入れているが、これはCでいえばVoid scalaでいえば、Unitみたいなもの。