Conversion of lock keyword of C# to C++
How can I implement lock keyword of C# in C++? The code in c# is as follows:
try
{
lock (matrixLock)
{
T开发者_JS百科hisTransformation.get_Renamed(matrix);
}
}
A. - Fast Answer
Assuming you have a Mutex
and a Lock
object (your mileage may vary):
#define MY_OWN_LOCK(mm_mutex) \
\
if(bool b_1227F2B8_136B_11E1_973D_7D0B4924019B = false) \
; \
else \
for(Lock lock_1227F2B8_136B_11E1_973D_7D0B4924019B(mm_mutex); \
!b_1227F2B8_136B_11E1_973D_7D0B4924019B; \
b_1227F2B8_136B_11E1_973D_7D0B4924019B = true)
Which can be used as:
Mutex mutex ;
void foo()
{
// not locked
MY_OWN_LOCK(mutex)
{
// locked
}
// not locked
}
B. - Detailed Answer
It depends on the library you'll be using.
B.1 - Pre-requisites
Let's assume you have:
- a
Mutex
object, which have alock()
and anunlock()
methods - a
Lock
object, which have a constructor with aMutex
as parameter, and calls itslock()
method at construction, andunlock()
method at destruction
So, you have something like:
class Mutex
{
public :
lock() ;
unlock() ;
// etc.
} ;
class Lock
{
Mutex & m_mutex ;
public :
Lock(Mutex & p_mutex) : m_mutex(p_mutex)
{ this->m_mutex.lock() ; }
~Lock()
{ this->m_mutex.unlock() ; }
// etc.
} ;
B.2 - Raw C+ use
If you are unfamiliar with C++'s RAII, your code will be like:
void foo()
{
// not locked
mutex.lock() ;
// locked !
mutex.unlock() ;
// not locked
}
This code is so wrong I won't ever discuss it (Google "exception safety" if needed).
B.3 - Raw C++ use
void foo()
{
// not locked
{
Lock lock(mutex) ;
// locked !
}
// not locked
}
B.4 - Macro-enhanced C++ use
With the following macro:
#define LOCK(mm_mutex) \
\
if(bool b = false) \
; \
else \
for(Lock lock(mm_mutex); !b; b = true)
You'll be able to write:
void foo()
{
// not locked
LOCK(mutex)
{
// locked !
}
// not locked
}
B.5 - Why so complicated ?
Most lock macros rely on the lock object to be testable. This either needs an implementation of the Safe Bool Idiom (which is overkill for the current use), or the need for the lock object to be castable to bool
, which brings its own (large) set of flaws to the class.
In the current implementation, the if
is used to declared the boolean that will control the for
's body execution, while the for
itself is used to declare the Lock
object itself.
I believe this pattern is called something like "C++ variable injection".
B.6 - Performance ?
Note that you're locking something, so the code inside the mutex.lock()
and mutex.unlock()
will take a lot more cycles than anything in the macro.
In non-optimized builds, the if
and for
jumps will show (e.g. try it step-by-step on a visual debugger), but in optimized build, the whole if
and for
will be optimized away (there's no difference between the assembly generated by the "raw C++ use" and the "macro-enhanced C++ use".
B.7 - Caution !!!
The macro above is simplified for educational purposes. To use it on production code, you must:
- "namespace" the macro name (i.e. prefix it with some kind of unique name, as the
BOOST_
part of theBOOST_FOREACH
macro) - make the boolean
b
and the Locklock
variable "unique" to make sure they won't collide with user code. I usually use a GUID/UUID suffix for that (e.g.b_ABCD_ABCD_AB_ABCDEF
andlock_ABCD_ABCD_AB_ABCDEF
)
B.8 - Sources
I first saw that pattern in an article (I believe by Andrei Alexandrescu), and indeed, I was searching for it when I stumbled on this SO question.
:-)
As soon as I find the source, I'll update this answer with the correct link.
Edit: Found the source!!!
- FOR_EACH and LOCK, by Eric Niebler and Anson Tsao : http://drdobbs.com/184401723 (or the printable version http://drdobbs.com/article/printableArticle.jhtml?articleId=184401723, better formatted IMHO)
- Safe Bool Idiom, by Bjorn Karlsson : http://www.artima.com/cppsource/safebool.html
You can use boost::mutex
and boost::scoped_lock
for this:
boost::mutex matrix_mutex;
// ...
try {
boost::scoped_lock lock(matrix_mutex);
// ... everything in this scope is now locked
} // ....
You can use macros and for-loops to give you a lock
keyword, although I would strongly advise against doing so, since that will break code that happens to use lock
as an identifier.
The C# lock
keyword is not a mutex. Instead, it calls Monitor::Enter()
Try this. Also look at MSDN reference.
// Request the lock, and block until it is obtained.
Monitor::Enter(m_inputQueue);
try
{
// Write your code here.
}
finally
{
// Ensure that the lock is released.
Monitor::Exit(m_inputQueue);
}
Note: this answer assumes you are targeting C++ CLI.
I was looking for the same coming from c# background and stumble upon this question a few times.. I know it's many years after it has been asked but now with c++11 you can use std::lock_guard and the syntax became very similar to c#
where in c# you have
object syncObj;
...
lock(syncObj){ ... }
in c++11 you can use a mutex as a sync and lock_guard similarly to lock
std::mutex m;
...
std::lock_guard<std::mutex> lock(m);{...}
if you
#define lock(mutex) std::lock_guard<std::mutex> lock(mutex);
then it would really look the same :)
Here is everything you need : Implementing a lock keyword in C++
C++ doesn't have a lock keyword, but you can make one yourself. Given a Mutex class which has Lock() and Unlock() member functions (and perhaps an IsLocked() for convenience) most C++ programmers would immediately write an AutoLock, somewhat like this:
class AutoLock
{
public:
AutoLock(Mutex& m): m_mutex(m) { m_mutex.Lock(); }
~AutoLock() { m_mutex.Unlock(); }
operator bool() { return m_mutex.IsLocked(); }
private:
Mutex& m_mutex;
};
Normal use of this thing would look like this:
{
AutoLock lock(m_mutex);
// my protected code here
}
But with a simple preprocessor trick you can make the syntax identical to C#:
#define lock(x) if (!(AutoLock _l = x)); else
C++ doesn't have a lock keyword. You could use a mutex.
You can't, no such key word exists, the closest thing you'll get is boost's scoped lock (which can use a boost mutex).
I would use the Boost synchronization library. If you can't for some reason, I'd hack something up like this:
class CriticalSection {
CRITICAL_SECTION m_cs;
public:
CriticalSection() {
::InitializeCriticalSection(&m_cs);
}
~CriticalSection() {
::DeleteCriticalSection(&m_cs);
}
void Lock() {
::EnterCriticalSection(&m_cs);
}
void Unlock() {
::LeaveCriticalSection(&m_cs);
}
};
class CriticalSectionLocker {
CriticalSection& m_cs;
bool m_bLocked;
public:
CriticalSectionLocker(CriticalSection& cs, bool bLockNow = true) : m_cs(cs), m_bLocked(bLockNow) {
if(bLockNow)
m_cs.Lock();
}
~CriticalSectionLocker() {
if(m_bLocked)
m_cs.Unlock();
}
void Lock() {
m_cs.Lock();
m_bLocked = true;
}
void Unlock() {
m_cs.Unlock();
m_bLocked = false;
}
};
Caveat Emptor: This code hasn't passed through a compiler. YMMV.
This code allows you to do stuff like this:
class SomeClass {
CriticalSection m_cs;
SomeResource m_resource;
public:
void SomeOperation() {
CriticalSectionLocker lock(m_cs);
m_resource.DoSomething();
}
};
The lock is locked in the scope of SomeClass::SomeOperation()
. You can also release the lock by calling its Unlock()
method, should you no longer need to hold the lock.
This code is by no means generalized. It can be tidied up with template-awesomeness, and make it general enough to make use of mutexes, and other operating system objects. But if want to go there, I'd recommend using the boost libraries instead.
You can use std::lock_guard
Here's the simplified example from that documentation:
std::mutex resource_mutex;
...
{
std::lock_guard<std::mutex> lock(resource_mutex); // The variable name ("lock") doesn't matter since it is not supposed to be used anywhere else.
...
// safe to use resource here
...
}
精彩评论