Click to See Complete Forum and Search --> : "Not Thread Safe" What's it mean?


RogerGarrett
July 11th, 2005, 07:30 PM
What exactly does it mean when something (CDaoDatabase, for example) is deemed "Not Thread Safe"? I realize that it probably means that you're going to run into problems if you use it within a thread. But what is it about that thing that makes it unsafe? Unsafe in what way? Why would anything not be safe to use within a thread?

wildfrog
July 11th, 2005, 07:46 PM
It means that if you have two or more thread working on the same instance of an object, then you'll run into problems.

In real life, that means that you'll have to "synchronize" your threads:

MyUnsafeClass pUnsafeObject;

Thread #1:
EnterCriticalSection(...); //Or some other synchronizing method
pUnsafeObject->UnsafeMethod();
LeaveCriticalSection(...);

Thread #2:
EnterCriticalSection(...);
pUnsafeObject->UnsafeMethod();
LeaveCriticalSection(...);

Synchronization comes with a performance penalty, so if all classes/methods were thread-safe it would be an unessecary performance overhead when used in single threaded applications.

So, by leaving the classes "not thread-safe" it's up to the developer to decide whether or not to synchronize...

- petter

Arjay
July 12th, 2005, 01:17 PM
What exactly does it mean when something (CDaoDatabase, for example) is deemed "Not Thread Safe"?A class is deemed thread safe if it can be safely shared between two threadsI realize that it probably means that you're going to run into problems if you use it within a thread.This is just a bit incorrect. Every object can be used in a thread. Thread safety becomes important when you are sharing objects between two or more threads But what is it about that thing that makes it unsafe? Unsafe in what way? Why would anything not be safe to use within a thread?We are going to assume the problems you are referring two are referring to objects (or simple types, like a string) shared between threads.

Let's demonstrate a non-thread safe string shared between two threads...

Without going into detail, the following output is an example of a string resource that is shared between two threads. One thread is writing to the string while a second thread is displaying it to the screen. The string gets corrupted because one thread hasn't finished copying to it before the second thread tries to display it.


-----------------------------------------------------------
------------------------------------------------------------
SlowCopy Example - Illustrates using non-synchronized shared memory
between two threads.

Source: The source string that will replace the dest string.
Dest: Original destination string that will be overwritten.

Secondary Thread Started
1) Original destination string that will be overwritten.
2) The snal destination string that will be overwritten.
3) The sourcdestination string that will be overwritten.
4) The source stination string that will be overwritten.
5) The source strintion string that will be overwritten.
6) The source string tn string that will be overwritten.
7) The source string that ring that will be overwritten.
8) The source string that will that will be overwritten.
9) The source string that will reat will be overwritten.
10) The source string that will replacill be overwritten.
11) The source string that will replace t be overwritten.
12) The source string that will replace the doverwritten.
13) The source string that will replace the dest written.
14) The source string that will replace the dest strtten.
15) The source string that will replace the dest string..
16) The source string that will replace the dest string.
Secondary Thread Exited

Destination string after SlowCopyNoSync:
The source string that will replace the dest string.

End of program!
------------------------------------------------------------
------------------------------------------------------------

Notice how the string slowly gets overwritten (in blue). In this example, each new line represents when the secondary thread wakes up to display the string. A shared resource that is properly synchronized would look something like:


-----------------------------------------------------------
-----------------------------------------------------------
SlowCopyNativeCS Example - Illustrates synchronizing shared memory
between two threads using a native critical section.
Source: The source string that will replace the dest string.
Dest: Original destination string that will be overwritten.

Secondary Thread Started
1) Original destination string that will be overwritten.
2) Original destination string that will be overwritten.
3) Original destination string that will be overwritten.
4) The source string that will replace the dest string.
Secondary Thread Exited

Destination string after SlowCopy:
The source string that will replace the dest string.

End of program!
------------------------------------------------------------
------------------------------------------------------------

In this output, notice how the secondary thread displays the thread completely. It's either displayed as the original string or the new string.


The above examples were from the following article: Win32 Thread Synchronization, Part I: Overview (http://www.codeguru.com/Cpp/W-D/dislog/win32/article.php/c9823/)

The second part of this, Win32 Thread Synchronization, Part II: Helper Classes (http://www.codeguru.com/Cpp/misc/misc/threadsprocesses/article.php/c9825/), introduce simple classes that assist with synchronization.

For example, if you wish to turn the CDaoDatabase class into a thread safe class, you could use the helper classes as follows:

First, derive from CLockableCS (uses a critical section to ensure thread safety)

class CTSDaoDatabase : public CDaoDatabase, public CLockableCS
{
}


Next, in each thread that accesses the class, you must first lock the thread before using it. (Let's assume that we've declare a CTSDaoDatabase m_pDB variable that is accessed from two threads)

{
// Lock the Database object (prevent any other thread
// from accessing it). Note: Unlock automatically happens on
// exit of scope
CAutoLockT< CTSDaoDatabase > lock( &m_pDB );

//
// Use the database object
//
} // Auto unlock happens here


As far as the example above, I'm not advocating that you actually lock the entire CDaoDatabase class (as you generally would prefer to lock the minimum of resources for as little time as possible) - in real code, you probably would structure your code differently. I chose this class as an example because you mentioned it and I wanted to illustrate how simple it is to add thread safety to a class.

Arjay

RogerGarrett
July 12th, 2005, 04:49 PM
Thank you for the advice. I am indeed using the EnterCriticalSection / LeaveCriticalSection approach to assure that only one thread is ever accessing the DAO database.

But I'm still getting problems.

In particular I'm getting:

DAO Call Failed.
m_pQueryDef->m_pDAOQueryDef->_30_OpenRecordset( COleVariant((long)m_nOpenType), COleVariant((long)m_nOptions), &m_pDAORecordset)
In file daocore.cpp on line 3338
scode = 800A0C07

It appears that the "scode" is the reason why it failed but I can't find anything that describes what an scode of 800A0C07 actually is.

MikeAThon
July 12th, 2005, 07:49 PM
The meaning of scodes (and hresult's) is described at "Structure of COM Error Codes" at http://msdn.microsoft.com/library/en-us/com/html/97e68708-eb62-4481-af03-cf8b80304103.asp

To decode your scode, use macros documented at "Using Macros for Error Handling" at http://msdn.microsoft.com/library/en-us/com/html/ad28eb80-cab9-4bec-9601-34660f6dcad4.asp

Mike

Axter
July 14th, 2005, 09:44 PM
What exactly does it mean when something (CDaoDatabase, for example) is deemed "Not Thread Safe"?A lot of experts get confuse about this terminology.



A class is considered thread safe if you can safely use multiple instances of the class in two or more threads.



An instance of the class is considered thread safe if you can use it safely in two or more threads.



There’s a big difference between these two things.

If someone who knows the correct terminology states that a class is thread safe, they don’t mean that you can declare an instance of the class and safely share it between multiple threads.



For example, VC++ 6.0 STL library is considered thread safe, but that doesn’t mean you can declare an instance of std::string, and then safely share it between multiple threads.

That just means that you can use multiple instances of std::string in multiple threads safely.

Some STL libraries are not thread safe, and you’ll commonly have problems with trying to declare multiple instances of std::string in multiple threads.

This problem commonly occurs when the reference counter in std::string is not implemented in a thread safe manner.

Many implementations use std::string type that has a reference counter, which allows you to share the same data between multiple instances of std::string. If you have a std::string that is not thread safe, the reference counter could be unsafely access simultaneously by two or more threads, which could cause memory leaks or the contents of a string to be deleted before the std::string object is deleted.

Such a class is not thread safe.



It’s not that hard to make a class that is thread safe.

It should be easy to make a class thread safe as long as your class doesn’t share any data between multiple instances.

Example:


class A_ThreadSafeClass
{
public:
A_ThreadSafeClass(int i)
{
m_i = i;
}
private:
int m_i;
};

class Not_ThreadSafe
{
public:
Not_ThreadSafe(int i)
{
++m_si;
m_i = i + m_si;
}
~Not_ThreadSafe()
{
--m_si;
}
private:
int m_i;
static int m_si;
};
int Not_ThreadSafe::m_si = 0;