1

I was wondering what is the better choice: it's assumed there is a trivially copyable object, let's say a queue data structure, that is used by several threads to pop/push data. The object provides only methods put/push, that can't be accessed by more than one thread the same time. Obviously if put is called, push can't be called neither.

Would you suggest to wrap the model into atomic type (if possible), or rather use mutexes?

Regards!

thatsme
  • 325
  • 1
  • 2
  • 10
  • 2
    If this is not a homework problem, you can use the wheels others have invented. Such as Boost.Lockfree or Intel's TBB's `concurrent_queue`. – Siyuan Ren May 02 '14 at 16:53
  • 2
    It depends on what your goal is. If you're looking to maximize efficiency, a lockless design will be more efficient. If you're concerned more with robustness and generality, synchronization with mutexes will be less susceptible to subtle errors, and will be easier to add functionality to in the future. For many purposes, though, either approach will work fine. – Jeremy Friesner May 02 '14 at 17:33
  • Nah, it's not a homework issue. I was reading the documentation of C++11 and this is the question that appeared in my mind. I would like to get an opinion of people who took this kind of decisions and know the limitations of atomic from practice. Thanks for the boost remark, but I consider this particular case using only C++11 std. – thatsme May 02 '14 at 19:37

2 Answers2

1

Atomic is hardware thing, whereas mutex is OS thing. Mutex will end up by suspending the task, even though in some cases mutex will behave as a spinlock for a short period of time aka "optimistic spin", see https://lore.kernel.org/all/56C2673F.6070202@hpe.com/T/ So, if you have small operations like incrementing a variable, aka "atomic", without waiting for other things which might take longer, then atomic is for you. If you want to (indefinitely) wait for some things to happen in other threads, polling for results via atomics, aka spinlock, might be a waste of CPU cycles therefore less cooperative, so it's better to use a mutex/condition variable which would suspend the task at a price of context switch latency.

  • A good lock-free queue will fall back to sleeping when full and/or empty. (It's not actually lock-free in a CS sense when it's full, regardless of whether you spin or sleep). e.g. with C++20 `foo.wait()` / `.notify()`, or by taking a mutex at that point, or just manually sleeping for short intervals. (Implementing a lock-free queue is non-trivial, and is nowhere near like using `atomic` or making each of its members separately atomic. See [Lock-free Progress Guarantees in a circular buffer queue](https://stackoverflow.com/q/45907210) for an analysis of one such queue) – Peter Cordes Oct 13 '22 at 09:24
  • But yes, using atomics for a single shared variable that's not part of a larger data structure is often best. But terminology nitpick, not all spin-wait loops are spin **locks**. A "spinlock" is an actual lock, not just waiting for a data-ready flag or something. Of course, using atomics don't imply you have to spin-wait; C++20 `foo.wait()` is a portable way to use OS-assisted sleep to wait for a value to change. But without that, yes the portable way to non-busy wait for something from another thread is a mutex (and condition variable). – Peter Cordes Oct 13 '22 at 09:28
0

Atomic is preferable for those kinds of cases. The atomic is a kind of operation supported by the CPU specifically whereas the other kinds of thread control tend to be implemented by the OS or other measures and incur more overhead.

EDIT: A quick search shows up this which has more info and is basically the same kind of question: Which is more efficient, basic mutex lock or atomic integer?

EDIT 2: And a more detailed article here http://www.informit.com/articles/article.aspx?p=1832575

Community
  • 1
  • 1
qeadz
  • 1,476
  • 1
  • 9
  • 17