STL (std) thread safety issues

P

Philip V Pham

These questions apply to std vector, map, and cout:

I am uncertain of the thread safety for reading/writing
for std templates. I know if all threads are reading
concurrently, it is thread safe. However, I have this situation:

Case 1: map

thread 1
---------
someMapOb [key] = value //inserting new pair

thread 2
---------

interator pos;

while (1==1)
{
for (pos = someMapOb.begin(); pos != someMapOb.end(), pos++ )
{
someValueOb = pos->second;

someValueOb.foo ();

}

sleep (1)
{


Is safe for case 1 if I do that? I don't care
if I miss a new inserted object in thread 2.
If a new object inserted into the map, will the
iterator be invalidated?
The reason that I omit a mutex is to maximize performance.

---------------
Case 2: queue

thread 1
--------
someQueue.push ( value );

thread 2
---------
while ( ! someQueue.isEmpty ()
{
// display a status indicator
// NOte, I don't pop or push the Q here
}


Is safe to call isEmpty() while the Q is being
pushed?

--------------------------

Case 3: cout

thread 1
--------
cout << "Thank you in advance for your help!" << endl;

thread 2
---------
cout << "Display....display ....." << endl;

I know the screen output might be messed up,
but would this concurrent cout cause a buffer overfloat
crash?

Thank you vey much for your help!
I look forward to hear from your solutions.
 
P

Pete C.

Philip said:
These questions apply to std vector, map, and cout:

I am uncertain of the thread safety for reading/writing
for std templates. I know if all threads are reading
concurrently, it is thread safe. However, I have this situation:
<snip>

Please don't multipost. If you must post in multiple groups, crosspost.
Standard C++ does not support threading, so your question is off topic for
this group. Please ask in a group for your implementation.

- Pete
 
D

Dave Townsend

STL has no built in thread support, so you'll have to extend the STL
code with your own synchronization mechanisms to use STL in
a multithreaded environment. Case1 and Case2 are clearly
unsafe in my opinion. One of the troubles with thread programming
is things can appear to work correctly in development, but strange
bugs appear in the field when the execution environment can vary .
You need to apply very strict logical reasoning
in thread programming and be very conservative in your assumptions.
After that, test your code on a multiprocessor machine.


Case 1 - this is not thread safe, thread 1 may be preempted
while performing the insertion, leaving the map in an undefined state,
iteration over the map would be undefined. Inserting an element into
a map rearranges the internal arrangement of elements, so if this is
preempted
the internal arrangement may not be in a valid state.

You might want to look at the double-locking pattern which provides
a more efficient mechanism to deal with locking shared data structures.
Basically, a flag is set/reset to indicate a lock mutex lock is in place, if
the flag is set, then thread gives up and try again later, otherwise the
thread
proceeds to acquire the lock and set the flag. This attempts to avoid the
costly
operation of trying to acquire the lock and failing.

I'd have to look it up, but I think the map iterators would not be
invalidated, that
is an iterator would still "point" to the same element after an insertion,
just
that the internal links to its neighbours could be different, - again, if
the rearrangment
of the elements is preempted, imagine what chaos will result if you try to
iterate
over the elements...!


case 2 - again, this is unlikely to work, the push operation is non-atomic
so could be preempted mid-way, so you might have a situation where an
element has been inserted intot the queue but the count of elements hasn't
been updated...I in your example this is "harmless"situation, but I don't
think I'd like
to have it in my fly-by-wire navigation system


case 3 - I don't know - cout could be implemented to serialize the two
calls, but might not be for performance reasons with different
implementations.

dave

Philip V Pham said:
These questions apply to std vector, map, and cout:

I am uncertain of the thread safety for reading/writing
for std templates. I know if all threads are reading
concurrently, it is thread safe. However, I have this situation:

Case 1: map

thread 1
---------
someMapOb [key] = value //inserting new pair

thread 2
---------

interator pos;

while (1==1)
{
for (pos = someMapOb.begin(); pos != someMapOb.end(), pos++ )
{
someValueOb = pos->second;

someValueOb.foo ();

}

sleep (1)
{


Is safe for case 1 if I do that? I don't care
if I miss a new inserted object in thread 2.
If a new object inserted into the map, will the
iterator be invalidated?
The reason that I omit a mutex is to maximize performance.

---------------
Case 2: queue

thread 1
--------
someQueue.push ( value );

thread 2
---------
while ( ! someQueue.isEmpty ()
{
// display a status indicator
// NOte, I don't pop or push the Q here
}


Is safe to call isEmpty() while the Q is being
pushed?

--------------------------

Case 3: cout

thread 1
--------
cout << "Thank you in advance for your help!" << endl;

thread 2
---------
cout << "Display....display ....." << endl;

I know the screen output might be messed up,
but would this concurrent cout cause a buffer overfloat
crash?

Thank you vey much for your help!
I look forward to hear from your solutions.
 
D

Dietmar Kuehl

Dave Townsend said:
STL has no built in thread support, so you'll have to extend the STL
code with your own synchronization mechanisms to use STL in
a multithreaded environment.

It is correct that the c++ standard does not require any multi-threading
capabilities. However, implementations for multi-threaded environments
typically have some support for multi-threading. In general, it is safe
to read data structures from multiple threads but changing a data
structure is allowed only for a thread with exclusive access to the data
structure. Note that you typically have to inject some form of
synchronization device after writing a data structure (eg. a mutex release)
even if you can guarantee that the data structure is not accessed from
multiple threads due to timing constraints: especially on multi processor
systems the modified data is not always visible to other processors until
it is explicitly written back.
Case1 and Case2 are clearly unsafe in my opinion.

All three cases are unsafe.
You might want to look at the double-locking pattern which provides
a more efficient mechanism to deal with locking shared data structures.

Note that the double-[checked] locking pattern does not work: it is a nice
idea but actually introduces hard to find multi-threading bugs - after all,
you explicitly handle multi-threading. I'm not a multi-threading expert
(unless you count the advice "Avoid using multi-threadings! Use other
approaches, e.g. multi-processing, where viable" as expertise...) to
describe the reasons sufficiently well myself, I have only read arguments
which make sense and reflect my experience with uses of the double-checked
locking pattern. You might want to look at discussions e.g. in
comp.lang.c++.moderated or google for a descriptions of the problems
I'd have to look it up, but I think the map iterators would not be
invalidated, that is an iterator would still "point" to the same element
after an insertion, just that the internal links to its neighbours could
be different, - again, if the rearrangment of the elements is preempted,
imagine what chaos will result if you try to iterate over the
elements...!

On multi-processors choas results even if the rearrangement is not
preempted: the changes are made to the processor's cache and it requires
some form of multi-threading synchronization to make the changes appear
for other processors. Typical processors for multi-processor machines often
have special instructions to flush the cache and invalidate the
corresponding cache sections of other caches. The user interface to these
instructions differ between platforms but a mutex always does the trick.
case 3 - I don't know - cout could be implemented to serialize the two
calls, but might not be for performance reasons with different
implementations.

It is very unlikely that this done: locking internal to a stream is simply
the wrong abstraction level. You need to put locking around the stream. You
might want to preformat the output into a thread-private string stream and
then merely insert the resulting string at once into a thread protected
'std::cout'. This has two positive effects: the output is not garbled and
the time 'std::cout' is blocked is minimized.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,226
Members
46,816
Latest member
nipsseyhussle

Latest Threads

Top