2

I'm getting a bit confused with the new threading in C++11. I get how I can use mutexes to stop two threads from operating on the same data at the same time, but what about assigning to that data?

Example!

class Foo
{
    std::string s;

    // This would be called on a seperate thread
    void Bar() { s = std::string( "blah blah blah" ); }
};

So what I'm asking is, because I'm assigning something to s, does the member variable s always stay in the same memory location, and assignments just change the internal data, in which case I just need a mutex? Or can I still get into situations with cached values and things which mean I need to start using atomic<> to make sure I have the up to date data? Or is that just for types like ints, or structs?

Joe
  • 726
  • 1
  • 8
  • 18

1 Answers1

4

Mutexes are guaranteed sufficient. Whatever magic is needed to make it work, they contain. So long as every thread that accesses or modifies any particular instance of s does so under protection of the same mutex, there will be no issue.

The only difference between access and assignment is this -- you do not need a mutex to prevent two threads from reading the same data at the same time. A mutex is only required when an object may be modified in one thread while another thread is or might be accessing it. Concurrent reads are permitted.

Note that this is the usual rule and applies to typical objects like std::string. One can, if one wishes to, make an object that breaks even with concurrent reads and even when one object is read in one thread while a different object of the same type is read in another thread. (But such objects are not useful and people just shouldn't make them.)

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Aren't those objects that can break when read only objects that are *written* to in a method *called* "read" ? If there is another possibility, I'd be happy to see it. – J.N. Jan 03 '12 at 05:11
  • Exactly. The operation is logically a read operation but includes physical write operations. (Consider, for example, an operation like `GetValueOfParameter(std::string paramName)` that, if the parameter isn't in memory, reads it from an SQL database. All kinds of writes occur there.) Or consider a read operation that, during its traverse to find the information you asked for, deletes any 'expired' objects it finds. – David Schwartz Jan 03 '12 at 05:11
  • 2
    Or `std::map::operator[]`, it's easy for a caller to forget that it's sometimes a write operation. I wouldn't say "shouldn't make them", so much as "must document what operations require external synchronization because they aren't pure reads even though they look like they might be". – Steve Jessop Jan 03 '12 at 09:40
  • Thanks a lot for clearing that up =) Just to make sure I've got this, if instead of a std::string I had a bool, and then on another thread I had a loop like while( !b ), would I be correct in thinking that the compiler might decide to cache b, so instead I'd need to use an atomic type rather than a mutex? – Joe Jan 03 '12 at 11:34
  • 1
    The compiler might decide to cache b, the CPU might, it can go wrong any number of ways. You could use either an atomic type or a mutex. Both provide the semantics required. (You don't have to figure out every way it could possibly go wrong and fix them all, just use a construct that has the semantics you need.) – David Schwartz Jan 03 '12 at 20:15
  • OK, I get atomic, but how would a mutex solve that? How does the compiler/CPU know that the mutex is related to b and so not to cache it? – Joe Jan 03 '12 at 21:09
  • 1
    In theory, it doesn't matter. The mutex implementation does whatever magic is needed to make it "just work". In practice, it's a bit complicated to explain. The short version is this: The compiler has no idea what the mutex lock/unlock functions do. So it cannot cache *anything* across calls to them. (Anything another thread could touch, the mutex lock/unlock functions could, as far as the compiler knows). As for the CPU, it's handled in hardware, through the cache coherency logic. (If you want a detailed answer, ask a specific question about that. This is a gross simplification.) – David Schwartz Jan 03 '12 at 23:25
  • That's more than good enough for me, thanks for all your help! =) – Joe Jan 04 '12 at 00:10