Re: New to CSingleLock
On Fri, 16 Mar 2007 06:38:46 GMT, Pat <none@none.none> wrote:
Hi all,
I'm having trouble making sense of the documentation for CCriticalSection
and CSingleLock.
Things I don't understand:
1) Do I need a CSingleLock, or is CCriticalSection enough by itself?
All you really *need* is CCriticalSection. The CSingleLock class provides
RAII semantics to ensure the mutex is unlocked no matter how the scope is
exited, be it falling off the end or early return, possibly due to an
exception. Note, however, that the CSingleLock ctor is painfully broken
because the default is to *not* acquire the lock, which is completely,
utterly backwards. Therefore, statements such as the following are legal
but do not acquire the lock:
CSingleLock lock(cs);
Instead, you need to say:
CSingleLock lock(cs, true);
2) Why do both classes have Lock() and Unlock() methods?
It can be useful to lock/unlock a CSingleLock at various points in an
algorithm, and as already mentioned, CCriticalSection can be used without
CSingleLock, so it needs these functions.
3) (Assuming I need CSingleLock...) Do I need to call
CSingleLock::Lock() explicitly, or does just creating the CSingleLock
object perform the lock?
See above.
4) Do I need to call CSingleLock::IsLocked() before entering the
"critical" code sections, or will just acquiring the lock cause the
necessary wait to happen?
In Windows, mutexes (both the CRITICAL_SECTION and kernel mutex types) are
recursive, which means the owning thread can lock them over and over (but
it must unlock them as many times as it locked them). On the other hand,
CSingleLock is not recursive, because it is merely a manager of a resource
and is intended to be used only as a local variable; IIRC, a locked
CSingleLock will assert if you attempt to lock it more than once. So you
would need to use IsLocked only when you're uncertain of the CSingleLock
status.
5) What happens if an exception is thrown in a "locked" section?
Unless something unlocks the mutex, it remains locked. See above.
Just in case it matters, here is the basic structure of my application:
It's a single MFC app (single process) with multiple threads. There is a
single, global object that is used by all the threads to share large
amounts of data. All its methods, like Read(), Write(), etc, are
completely "thread-unsafe" right now, because the data is stored in a
complicated way.
When making things thread-safe, be sure to do it at the right level. For
example, making reads/writes thread-safe is insufficient for things like
this:
cout << a << b << c;
This reduces to something like this:
write(cout, a);
write(cout, b);
write(cout, c);
Even if the individual writes are thread-safe, other threads writing to
cout can intersperse their output with these write calls. The solution is
to protect the whole sequence, e.g.
{
CSingleLock lk(cs);
cout << a << b << c;
}
--
Doug Harrison
Visual C++ MVP