最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

multithreading - Atomically modifying multiple IORefs in concurrent Haskell - Stack Overflow

programmeradmin0浏览0评论

The documentation for atomicModifyIORef states the following:

This function is useful for using IORef in a safe way in a multithreaded program. If you only have one IORef, then using atomicModifyIORef to access and modify it will prevent race conditions.

Extending the atomicity to multiple IORefs is problematic, so it is recommended that if you need to do anything more complicated then using MVar instead is a good idea.

I'm not sure to understands what this precisely mean. Suppose for instance I have two completely independent IORefs, is atomicity not guaranteed ? If this is the case, what is the point of atomicModifyIORef ? It seems very dangerous to use...

Now I'm aware of STM, but for CAS (compare-and-swap) implementation, which is what I need, it apparently runs slower. Indeed, this article says, p. 5

In terms of performance, the atomicModifyIORef version of CAS is vastly superior to the STM encoding of CAS.

So my questions are the following :

  1. When exactly is the atomicity broken when using atomicModifyIORef multiple IORefs in a multithreaded environment ?
  2. Why is it "problematic" to extend atomicity to multiple IORefs ? Any reference on the subject ?

The documentation for atomicModifyIORef states the following:

This function is useful for using IORef in a safe way in a multithreaded program. If you only have one IORef, then using atomicModifyIORef to access and modify it will prevent race conditions.

Extending the atomicity to multiple IORefs is problematic, so it is recommended that if you need to do anything more complicated then using MVar instead is a good idea.

I'm not sure to understands what this precisely mean. Suppose for instance I have two completely independent IORefs, is atomicity not guaranteed ? If this is the case, what is the point of atomicModifyIORef ? It seems very dangerous to use...

Now I'm aware of STM, but for CAS (compare-and-swap) implementation, which is what I need, it apparently runs slower. Indeed, this article says, p. 5

In terms of performance, the atomicModifyIORef version of CAS is vastly superior to the STM encoding of CAS.

So my questions are the following :

  1. When exactly is the atomicity broken when using atomicModifyIORef multiple IORefs in a multithreaded environment ?
  2. Why is it "problematic" to extend atomicity to multiple IORefs ? Any reference on the subject ?
Share Improve this question edited Nov 19, 2024 at 18:41 141592653 asked Nov 19, 2024 at 18:35 141592653141592653 7235 silver badges14 bronze badges 6
  • 3 This isn't anything special to Haskell. CAS is just very tricky to extend to multi-word scenarios generally. That is: if you have two IORefs, each CAS is atomic independently, but if you want to change the value of both IORefs in a way such that in the rest of the program either both changes have happened or neither have, that is very hard to get right. There's all sorts of low-level memory-op reordering, cache-coherency, and similar wrinkles not to mention the high-level issue of designing a protocol even if you make very strong assumptions about memory ordering. – Daniel Wagner Commented Nov 19, 2024 at 19:01
  • 1 The book "Parallel and Concurrent Programming in Haskell" oreilly/library/view/parallel-and-concurrent/9781449335939/… says that " You should think of atomicModifyIORef as a very limited version of STM; it performs a transaction on a single mutable cell. Because it is much more limited, it has less overhead than STM." – danidiaz Commented Nov 19, 2024 at 19:50
  • Note as well that the IORef API doesn't provide a way of "doing IO" while atomically modifying the IORef. The functions that are applied to the IORef value are pure. So it's not even possible to modify one IORef while in the process of modifying another. (MVars do let you perform IO effects while "holding" the MVar.) – danidiaz Commented Nov 19, 2024 at 19:56
  • @DanielWagner thank you your comment. Yes this makes sense, and actually in my case I'm perfectly fine with having moments in the rest of the program where one change has happened and the other has not. So if both IORefs are completely independent in the sense that I will never need to commit a change to both of them "simultaneously" the atomicity is preserved ? – 141592653 Commented Nov 19, 2024 at 21:47
  • 1 @141592653 "So if both IORefs are completely independent in the sense that I will never need to commit a change to both of them 'simultaneously' the atomicity is preserved?" Yes. – Daniel Wagner Commented Nov 19, 2024 at 22:06
 |  Show 1 more comment

1 Answer 1

Reset to default 1

A modify of an IORef embodies a read followed by a write. So if you write a naïve implementation of modify like this:

do
  old <- readIORef myVar
  writeIORef myVar $ f old

then you have a potential race condition if two threads execute this code at the same time.

atomicallyModifyIORef guarantees to do this operation atomically, so the first thread will complete the update before the second one starts.

However if you have two IORefs then you can't do it this way. Say you want to transfer some value from one to the other, you can decrement one atomically, and then increment the other atomically, but between those two operations there is a period when the sum is in transit, and so the state is inconsistent.

Extending atomicity to multiple IORefs would require a lot of extra overhead for tracking which IORefs are included in a transaction, storing values in case of a rewind etc. This would be paid for any use of IORefs even if no atomic transactions are done. So its a bad idea. That's what TRefs are for.

发布评论

评论列表(0)

  1. 暂无评论