STL & multithreading

U

Uenal Mutlu

May you explain what you mean with the above, using std::vector as an example?

What he said is IMO self-explanatory... :)
His method cannot make the inner-workings, ie. the implementation, of
any class used thread-safe; and this is also not necessary, since it
is the class designers (the implementers) job.
Ie. for your example: what inside vector happens is up to the designer
of it. What Axter's wrapper does is that it can make accesses to the
object thread-safe, even only for the duration of a method-call (or element-wise)
on a "on-demand" basis (ie. not simply locking the whole object; though this
is possible too). I must say, a clever idea, and it works. Of course there
is a small overhead for locking/unlocking but this is minimal and unavoidable.
His method can make everything thread-safe, ie. the whole STL and
also anything else too.
I would like to congratulate him for his brilliant idea. He should be awarded
the nobel price in CS; it solves a big problem in the programming land.
 
P

Pete Becker

Uenal said:
What Axter's wrapper does is that it can make accesses to the
object thread-safe, even only for the duration of a method-call (or element-wise)
on a "on-demand" basis (ie. not simply locking the whole object; though this
is possible too). I must say, a clever idea, and it works. Of course there
is a small overhead for locking/unlocking but this is minimal and unavoidable.
His method can make everything thread-safe, ie. the whole STL and
also anything else too.
I would like to congratulate him for his brilliant idea. He should be awarded
the nobel price in CS; it solves a big problem in the programming land.

I don't know if you're serious, but lest anyone be led astray: This
"brilliant idea" is obvious to anyone skilled in the art, and it has
been applied in many places. It does not solve the fundamental problems
of writing fast, robust multi-threaded code. Beware of anyone claiming
to have found the silver bullet for multi-threading; there's fifty years
of history of silver bullets, and all of them are now badly tarnished.
 
U

Uenal Mutlu

What he said is IMO self-explanatory... :)
His method cannot make the inner-workings, ie. the implementation, of
any class used thread-safe; and this is also not necessary, since it
is the class designers (the implementers) job.
Ie. for your example: what inside vector happens is up to the designer
of it. What Axter's wrapper does is that it can make accesses to the
object thread-safe, even only for the duration of a method-call (or element-wise)
on a "on-demand" basis (ie. not simply locking the whole object; though this
is possible too). I must say, a clever idea, and it works. Of course there
is a small overhead for locking/unlocking but this is minimal and unavoidable.
His method can make everything thread-safe, ie. the whole STL and
also anything else too.
I would like to congratulate him for his brilliant idea. He should be awarded
the nobel price in CS; it solves a big problem in the programming land.

BTW, the example he gave is maybe hard to understand but I tried
it bare-bones without the MFC-stuff and it works flawlessly.
 
U

Uenal Mutlu

I don't know if you're serious, but lest anyone be led astray: This
"brilliant idea" is obvious to anyone skilled in the art, and it has
been applied in many places. It does not solve the fundamental problems
of writing fast, robust multi-threaded code. Beware of anyone claiming
to have found the silver bullet for multi-threading; there's fifty years
of history of silver bullets, and all of them are now badly tarnished.

His method makes the use of any object in a generic way thread safe.
In my eyes this is a big step forward.
From my own experience: until now I hesitated to use many of the
STL classes because of this problem (thread-safety, concurrently
accessing/modifying objects). Now, with this method all these
problems are solved; with this method STL can safely be used in
all multithreading apps.
 
A

Axter

Ioannis said:
May you explain what you mean with the above, using std::vector as an example?

It's hard to explain in writing.
Check out the following link:
http://code.axter.com/TestCaseAppforThreadSafeObject_sourcecode.zip

The above link is a zip file with VC++ project source code.
I've modified the ThreadSafeObject class so that you can stub out the
synchronization code.
If you compile and run the code as-is, you'll see that it runs just
find, and does not crash.

You can then stub out the sync code by uncommented out the following
line in ThreadSafeObject.h file
//#define STUB_OUT_SYNC_CODE_

Then recompile the code with above define, and then run it.
You'll see that it crashes right away.
Even though you're using a thread-safe VC++ STL class, the application
will still crash.
This happens because the class is thread safe, but that does not mean
an instance of the class is thread safe.
To make your instance thread safe, you need to add a thread safe
wrapper around it, or you need to add application level synchronization
code.
IMHO, a thread safe wrapper class is much more OO and much safer then
using an application level code.
 
P

Pete Becker

Uenal said:
His method makes the use of any object in a generic way thread safe.
In my eyes this is a big step forward.
From my own experience: until now I hesitated to use many of the
STL classes because of this problem (thread-safety, concurrently
accessing/modifying objects). Now, with this method all these
problems are solved; with this method STL can safely be used in
all multithreading apps.

I stand by what I said: writing fast, robust multi-threaded applications
requires application-level design. No library can do it for you.
Libraries can provide tools to make implementing that design easier, but
they cannot replace it.
 
A

Axter

Pete said:
I don't know if you're serious, but lest anyone be led astray: This
"brilliant idea" is obvious to anyone skilled in the art, and it has
been applied in many places.

I have never seen a general purpose thread safe wrapper class. If it's
been applied in many places, then I'm sure you have many examples with
links.
Please be kind enough to post a few.
It does not solve the fundamental problems
of writing fast, robust multi-threaded code. Beware of anyone claiming
to have found the silver bullet for multi-threading; there's fifty years
of history of silver bullets, and all of them are now badly tarnished.

I totally agree that the ThreadSafeObject wrapper class does have an
over-head, however this over-head is minimal, especially compared to
synchronization API functions.

I never claim that the class is a one size fit's all class. It's a
general purpose class. And like most general purpose implementation,
it's designed for common requirements, and not for special
requirements.
 
A

Andre Kostur

His method makes the use of any object in a generic way thread safe.
In my eyes this is a big step forward.
From my own experience: until now I hesitated to use many of the
STL classes because of this problem (thread-safety, concurrently
accessing/modifying objects). Now, with this method all these
problems are solved; with this method STL can safely be used in
all multithreading apps.

Thing is... it's not a new step. Look up the concept of RAII or Resource
Acquisition Is Initialization. The management of any resource (not just
mutexes/critical sections) should be wrapped into a class of some sort
(generally speaking). This goes for files (see fstream), memory (see
std::auto_ptr or boost::shared_ptr), or mutexes (see ACE_Guard<>, if
you're using the ACE libraries, other threading platforms probably have
similar constructs).

Heck.. I recall an article in CUJ a couple of years ago that described a
std::auto_handle which templated against any type to do all of this.
 
A

Andre Kostur

If you have to perform several functions who's results depend on each
other, then you can lock access to the object by using a RefLock
variable.

However, that is not how you presented your class to be used. To quote:

Once the wrapper class is declared, it acts like a pointer.

Example Usage
ThreadSafeObject<vector<int> > MyThreadSafeVectorInt(new vector
<int>);
MyThreadSafeVectorInt.GetLockedObject()->push_back(123);
cout << MyThreadSafeVectorInt.GetLockedObject()->at(0) << endl;

Note, no mention in here about a RefLock type, and you specifically
pointed out that your class was to be used like a pointer. Pointers
don't have a RefLock member....
For the duration of the RefLock variable, the object is locked and
only the one thread can access it.
You can then safely do multiple functions who's results depend on each
other, and do it in a thread safe way.

So RefLock is a class which implements RAII. Easy enough, people have
been doing it for years.
example:

ThreadSafeObject<std::vector<int> >::RefLock MyLockedVec
MyRefThreadSafeOjbectTestCase.MyThreadSafeVectorInstance.GetLockedObjec
t(); if (MyLockedVec->size() > 10000 && MyLockedVec->size()&1) //Due
odd numbers
{
MyLockedVec->pop_back();

For a more complex example see following Unit Test class that is able
to perform all above senerios in an thread safe way.
http://code.axter.com/ThreadSafeOjbectTestCase.h
http://code.axter.com/ThreadSafeOjbectTestCase.cpp

The above unit test has 13 threads accessing the same object, and
using the ThreadSafeObject class to allow the objects to be access in
a thread safe manner.

This OO approach is safer, and it makes application locks redundant.

Uh, no. Then you run into the (potential) issue of keeping the contents
of multiple containers consistent with each other. Either you have to
violate the semantics of what your locks are holding, or you need a
broader-scoped lock than your container locks, and makes your container
locks redundant.

Let us assume a list<> A, and a map<> which holds pointers or iterators
into A. Simply making the list<> threadsafe in your manner and the map<>
similarly threadsafe in your manner wouldn't provide thread safety for
the application. You would either:

1) Risk inconsistency by having one thread remove an object from one
container, when a second thread comes along and needs to do something
with this now inconsistent result.
2) Hold locks longer than necessary. Such as holding the list<>'s lock
during the duration of the operations on the map<>. Which also opens you
up to potential deadlock conditions. And, why should I need to hold the
list<> lock to work on a different object?

Here's where a broader-scoped lock would protect both the list<> and the
map<>. You lock it once, do the operations on the containers, then
unlock it. Of course this lock would be used with an "RAII object" to
manage the unlocking and locking. And if this is being used, then the
individual locks on the list<> and the map<> are redundant, as the two
containers would only be operated on within the context of this broader
lock.
 
A

Axter

Pete said:
I stand by what I said: writing fast, robust multi-threaded applications
requires application-level design. No library can do it for you.
Libraries can provide tools to make implementing that design easier, but
they cannot replace it.

This sounds too much like a procedural based mind-set.
If you want to write code that is maintainable, fast, and truly robust,
then you would take a more object orientated approach.
Only when you have proven through profiling, that your OO approach is
hindering performance, should you try to by pass it with a less
maintainable procedural base method.
I'm surprise someone with your background would have such an
inflexible point of view on this topic.
 
P

Pete Becker

Uenal said:
From my own experience: until now I hesitated to use many of the
STL classes because of this problem (thread-safety, concurrently
accessing/modifying objects). Now, with this method all these
problems are solved; with this method STL can safely be used in
all multithreading apps.

The idea of a "monitor" has been around for a long time. In C++
parlance, a monitor is an object of a type with all of its data private
whose accessible member functions are all synchronized. Java has them,
in the form of synchronized classes. What people quickly find is that
monitors are too expensive. Most Java classes that require object-level
synchronization these days are written with some synchronized methods
(not the entire class), some methods that do some preparation work
before locking (since in many cases the code determines that the part
that requires a lock doesn't have to be done), and with the
synchronization object exposed for the times when you need it.

Like all silver bullets, monitors haven't caught on. They solve the
relatively easy problem of assuring that one chunk of data isn't subject
to data races; they don't solve the harder problem of assuring that
there are no harmful data races in an application, and doing so without
making the application prohibitively slow.
 
U

Uenal Mutlu

I stand by what I said: writing fast, robust multi-threaded applications
requires application-level design. No library can do it for you.
Libraries can provide tools to make implementing that design easier, but
they cannot replace it.

I would say that application level locking mostly leads to slower
code because you are locking too much; in the worst case
the whole application.
Even if you lock a class which has say 5 vectors, you still have
locked too much. It is better to lock on a per object basis, ie. here
each vector individually, so the other threads can still use the other
vectors. Of course it heavily depends on the business logic of the application.
But with this method you have the possibility to lock the whole object
(which can contain other objects), or lock each sub-object therein individually.
Depending on the business logic one can achieve more performance
by locking as less as possible, and this can be done best by locking at
atomic object level.
 
I

Ioannis Vranos

Uenal said:
What he said is IMO self-explanatory... :)
His method cannot make the inner-workings, ie. the implementation, of
any class used thread-safe; and this is also not necessary, since it
is the class designers (the implementers) job.
Ie. for your example: what inside vector happens is up to the designer
of it. What Axter's wrapper does is that it can make accesses to the
object thread-safe, even only for the duration of a method-call (or element-wise)
on a "on-demand" basis (ie. not simply locking the whole object; though this
is possible too). I must say, a clever idea, and it works. Of course there
is a small overhead for locking/unlocking but this is minimal and unavoidable.
His method can make everything thread-safe, ie. the whole STL and
also anything else too.
I would like to congratulate him for his brilliant idea. He should be awarded
the nobel price in CS; it solves a big problem in the programming land.


What he said in the above is not self-explanatory. I did not ask about his wrapper class,
and he did not talk about it in the above.

What he said is that in the case of a compiler providing a thread-safe C++ standard
library implementation, "doesn't mean that the classes themselves are thread-safe but only
the objects". Which doesn't make much sense by itself for the standard library, that's why
I asked him if he may explain what he means for std::vector as an example.
 
I

Ioannis Vranos

Uenal said:
His method makes the use of any object in a generic way thread safe.
In my eyes this is a big step forward.
From my own experience: until now I hesitated to use many of the
STL classes because of this problem (thread-safety, concurrently
accessing/modifying objects). Now, with this method all these
problems are solved; with this method STL can safely be used in
all multithreading apps.

May you provide an example of a C++ compiler supporting multithreading that does not
provide a thread-safe standard library?

Or more specifically, what C++ compiler do you use that supports multithreading and does
not provide a thread-safe standard library?
 
I

Ioannis Vranos

Uenal said:
I would say that application level locking mostly leads to slower
code because you are locking too much; in the worst case
the whole application.
Even if you lock a class which has say 5 vectors, you still have
locked too much. It is better to lock on a per object basis, ie. here
each vector individually, so the other threads can still use the other
vectors. Of course it heavily depends on the business logic of the application.
But with this method you have the possibility to lock the whole object
(which can contain other objects), or lock each sub-object therein individually.
Depending on the business logic one can achieve more performance
by locking as less as possible, and this can be done best by locking at
atomic object level.


Yes, but still it is application-level locking. At least this is how I call it. What do
you consider as application-level locking?


As non-application level locking I consider construct-level locking like the one provided
by OpenMP standard. An example I have provided in this thread is this:


#include <vector>


int main()
{
using namespace std;

vector<int> vec(100);

// ...

#pragma omp for
for(vector<int>::size_type i=0; i<vec.size(); ++i)
vec= i;
}



With this OpenMP #pragma directive, you provide the guarantee that each element
access/modification is independent of the others, so the compiler generates more than 1
threads for the assignments. In this code for example, when using one of the upcoming
multi-core AMD/Intel processors, or an existing multi-processor system, the assignments
will take advantage of the two or more processors/cores and finish in less time than in
the usual single thread case.
 
I

Ioannis Vranos

A correction:

Ioannis said:
What he said in the above is not self-explanatory. I did not ask about
his wrapper class, and he did not talk about it in the above.

What he said is that in the case of a compiler providing a thread-safe
C++ standard library implementation, "doesn't mean that the objects
themselves are thread-safe but only the classes". Which doesn't make
 
P

Pete Becker

Uenal said:
I would say that application level locking mostly leads to slower
code because you are locking too much; in the worst case
the whole application.

No, that's not what application-level locking means. It means designing
the lock strategy with the entire application in mind. For example:

void log(const char *msg)
{
logfile << msg << '\n';
cout << msg << '\n';
}

Now, if you want to insure that both streams get all of their messages
in the same order, using object-level locks you have to first lock the
logfile, then lock cout, then write the text, then unlock the locks. If
you're not married to the notion of object locks, though, it's clear
that simply locking a lock on entry to the function and unlocking it on
exit does the job, and with only one lock instead of two.
 
U

Uenal Mutlu

However, that is not how you presented your class to be used. To quote:

Once the wrapper class is declared, it acts like a pointer.

Example Usage
ThreadSafeObject<vector<int> > MyThreadSafeVectorInt(new vector
<int>);
MyThreadSafeVectorInt.GetLockedObject()->push_back(123);
cout << MyThreadSafeVectorInt.GetLockedObject()->at(0) << endl;

Note, no mention in here about a RefLock type, and you specifically
pointed out that your class was to be used like a pointer. Pointers
don't have a RefLock member....

You should study the ThreadSafeObject.h file.
There are two examples given, incl. RefLock.
 
I

Ioannis Vranos

Axter said:
It's hard to explain in writing.
Check out the following link:
http://code.axter.com/TestCaseAppforThreadSafeObject_sourcecode.zip

The above link is a zip file with VC++ project source code.
I've modified the ThreadSafeObject class so that you can stub out the
synchronization code.
If you compile and run the code as-is, you'll see that it runs just
find, and does not crash.

You can then stub out the sync code by uncommented out the following
line in ThreadSafeObject.h file
//#define STUB_OUT_SYNC_CODE_

Then recompile the code with above define, and then run it.
You'll see that it crashes right away.
Even though you're using a thread-safe VC++ STL class, the application
will still crash.
This happens because the class is thread safe, but that does not mean
an instance of the class is thread safe.
To make your instance thread safe, you need to add a thread safe
wrapper around it, or you need to add application level synchronization
code.
IMHO, a thread safe wrapper class is much more OO and much safer then
using an application level code.


OK thanks for providing this example. However I only know .NET programming and not
MFC/Win32 so I can't figure out much of this mess. Also I could not find the entry point
of the application (could not find any main(), WinMain() etc). May you tell me which is
the entry point of the application and also tell me which is the vector used?


..NET multithreading by the way is done like this (you can place the try and catch blocks
inside the body of the member function/function as most people do, but I like this ISO C++
style):


#using <mscorlib.dll>

using namespace System;
using namespace System::Threading;


__gc class SomeClass
{
int index;

//...

public:

// ...


void DoSomething() try
{
Monitor::Enter(this);

// Modify index

Monitor::Exit(this);
}

// Exceptions are handled inside the specific thread locally
catch(Exception *)
{
Monitor::Exit(this);
// ...
}


void DoSomethingElse() try
{
Monitor::Enter(this);

// Modify index

Monitor::Exit(this);
}

// Exceptions are handled inside the specific thread locally
catch(Exception *)
{
Monitor::Exit(this);
// ...
}

// ...
};


int main() try
{
SomeClass *ps= __gc new SomeClass;


Thread *pthread1= __gc new Thread ( __gc new ThreadStart(ps, &SomeClass::DoSomething) );

Thread *pthread2= __gc new Thread ( __gc new ThreadStart(ps,
&SomeClass::DoSomethingElse) );


//Start execution of ps->DoSomething()
pthread1->Start();

//Start execution of ps->DoSomethingElse()
pthread2->Start();
}


catch(Exception *pe)
{
Console::WriteLine("{0}", pe->Message);
}


The above code compiles.
 

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,770
Messages
2,569,586
Members
45,097
Latest member
RayE496148

Latest Threads

Top