10

の署名modifyIORefは非常に簡単です。

modifyIORef :: IORef a -> (a -> a) -> IO ()

残念ながら、これはスレッドセーフではありません。この問題に対処する代替手段があります。

atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b

これら2つの機能の違いは正確には何ですか? 別のスレッドから読み取られる可能性のある をb変更するときに、パラメーターをどのように使用すればよいですか?IORef

4

4 に答える 4

12

余分なパラメーターは、戻り値を提供するために使用されます。たとえば、a に格納されている値をアトミックに置き換えてIORef、古い値を返したい場合があります。次のようにできます。

atomicModifyIORef ref (\old -> (new, old))

返す値がない場合は、次を使用できます。

atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
atomicModifyIORef_ ref f =
    atomicModifyIORef ref (\val -> (f val, ()))

と同じ署名がありmodifyIORefます。

于 2016-09-22T13:36:37.770 に答える
2

これが私がこれをどのように理解するかです。ブラケットイディオムに従う関数を考えてください。

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r

これらの関数は、関数を引数として取り、その関数の戻り値を返します。atomicModifyIORefそれに似ています。関数を引数として取り、その関数の戻り値を返すことを意図しています。複雑な点が 1 つだけあります。引数関数は、 に格納される新しい値も返さなければなりませんIORef。そのため、atomicModifyIORefその関数から 2 つの値を返す必要があります。もちろん、このケースはブラケットのケースと完全に似ているわけではありません (たとえば、IO関係がない、例外の安全性を扱っていないなど) が、この類推からアイデアが得られます。

于 2016-09-24T20:54:21.400 に答える
2

コメントで述べたように、同時実行性がなければ、次のようなものを書くことができます

modifyAndReturn ref f = do
  old <- readIORef ref
  let !(new, r) = f old
  writeIORef r new
  return r

しかし、並行コンテキストでは、他の誰かが読み取りと書き込みの間の参照を変更する可能性があります。

于 2016-09-25T00:03:13.600 に答える