Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

Concurrency, part way too many - concurrency and the C runtime library.

Concurrency, part way too many - concurrency and the C runtime library.

  • Comments 10
For some reason, I can't seem to let this concurrency thing go, I keep on thinking of more and more relevant topics - there are a lot of issues surrounding concurrency.

I realized today that my concurrency series didn't talk at all about libraries and concurrency, and I want to address that issue in a couple of articles.  To help avoid feature creep, here a brief outline of the articles:

  1. Concurrency and the C runtime library
  2. Concurrency and the Win32 API set
  3. Concurrency and Windows (GDI and User)

That's it, I'm going to do my best to limit myself to those three articles - given my recent history on this topic, I'm not sure I'll succeed, but I'm going to try.

Ok, concurrency and the C runtime library (to be specific, the Microsoft C runtime library, YMMV for others)...

The C language specification is intentionally agnostic with respect to threads - there are no mentions of multithreading issues in the C specification at all, it's left up to the implementation.

To deal with potential multithreading issues, Microsoft produces three different versions of the C runtime library (actually there are 6 different versions - the three variants I'm describing come in debug and retail versions).  Those three versions are:

  • Non thread safe C runtime library (-ML)
  • Thread safe C runtime library (-MT)
  • DLL C runtime library (-MD)

The reason that there are three different libraries is that the performance characteristics of the three are different.  The non thread safe C runtime library is faster than the thread safe library because (a) it doesn't do locking internally, and (b) the C runtime library contains several functions (like strtok and errno) that are stateful (in other words, they rely on the results of previous operations that occurred on the thread).  The second is actually more important than the first - the overhead of locks for a single threaded application is quite small, but the stateful operations need to be implemented differently - they can't rely on global variables to hold their state, and instead have to rely on thread local storage.

The thread safe C runtime library exists to resolve the above issues - in the non thread-safe C runtime library, errno is a global variable, in the thread-safe C runtime library, it's a call to the function _errno() - the differences are hidden by a macro in the multithread case.  If you're writing a multithreaded application, it's critical that you use the thread safe C runtime library instead of the non thread safe library.

The third library (the DLL C runtime library) is also thread safe, and has the additional value of sharing code with all other applications that use the C runtime library in your process.  I've written about the DLL C runtime library before, so I'll just include that by reference.  I strongly recommend using the DLL C runtime library, simply because of the code sharing opportunities.

Microsoft has a set of articles describing multithreading and the C runtime library in more detail here.

Now, having said that, what about C++ and/or ATL?  In particular, what about the STL and ATL collection classes?  It turns out that they are explicitly NOT thread safe.  This is largely an artifact of their implementation - the ATL and STL template libraries are built in-line, they're not in a runtime library, and it's not trivial to be able to ensure concurrency (it's possible, for example, the iostream classes guarantee thread safety if you're using the thread safe runtime library, and the STL collections have specific semantics regarding thread safety that can be found here).

I've separately been informed that the ATL collection libraries for ATL 7.0 (CStringT, CAtlMap, CAtlMap, CAtlArray, CRBMap and CRBMultiMap at a minimum) have the same thread safety guarantees as the STL - a single object is safe for either one writer or many readers, but it's up to the user of the template to ensure that the guarantee is met.  And, as Pavel Lebedinsky pointed out the rationale for this can be found here.

 

Edit: Added clarification of the concurrency rules for ATL collections.

 

  • MSDN has pretty complete documentation for the thread safety of VC++'s STL implementation, and it's exactly what you would think it would be (e.g. reads from a container by multiple threads are allowed, multiple writes are not).
  • Slightly OT, but Google released some open source tools, and amongst them is a performance monitor/tuner for multi-threaded apps. I've not checked it out in any detail, but I imagine that Google are amongst the best when it comes to dealing with concurrency and scaling issues.

    Check http://code.google.com/ for more details...
  • ATL uses the locking model of the object you create to guard resources against concurrency issues.

    it depends on your base class definition

    if you base your object on CComObjectRootEx<CComMultiThreadModel> the atl generated code is (apparently) thread safe

    there are many variations on the threading model for different circumstances

    the tricks of generating code this way are neat I think - but it is difficult to grasp as a new comer to the templates
  • Your concurrency articles have been great. As long as your considering more of them ... I was hoping you'd touch on how threads get assigned to processors (or cores). Do different quanta on the same thread potentially execute on different processors? Is there a way to influence the thread-processor assignment? Can I create a home-brew thread pool with one worker thread assigned to each processor?
  • The ThreadModel param to CComObjectRootEx determines how thread-safe your COM object's reference counting is; it doesn't affect any methods outside of the three IUnknown methods. So if you have a COM object running in an MTA, it's up to you to ensure thread-safe access to the object's data.
  • > what about the STL and ATL collection
    > classes? It turns out that they are
    > explicitly NOT thread safe. This is largely
    > an artifact of their implementation

    It's actually a deliberate design decision (at least in the case of STL):

    http://www.sgi.com/tech/stl/thread_safety.html
  • > The C language specification is
    > intentionally agnostic with respect to
    > threads

    I don't think so. The way the C language specification defines sequence points, the theoretical possibility of multithreading in between sequence points is allowed by the standard. (Ordinarily the overhead of creating and synchronizing threads would make that a big loser, so it's best ignored.) But the standard doesn't allow anything more than that. Any visible effect from multithreading on a more practical scale would violate the standard.

    If you'd written "antagonistic" instead of "agnostic" I'd agree, but I'm not sure if I'd call it intentional or not. Avoidance of mentioning threads was intentional, but the entire result might not have been.

    Pragmatically speaking you just don't do standards conformance and multithreading at the same time, you choose one or the other (or neither). In fact everyone depends on not being fully standards conforming, whether or not they're using threads.
  • Good point Norman, and well spoken.

    What I've not been able to find is a statement about the thread safety of ATL - I found thread safety statements for STL (quoted in the article).

    I'm hoping someone comes up with something (either internal (yes, I did ask) or external)..
  • PingBack from http://pooltoysite.info/story.php?id=6803

Page 1 of 1 (10 items)