k*******t 发帖数: 202 | 1 class SyncBuf {
public:
void Thread1();
void Thread2();
private:
typedef vector BufT;
volatile BufT buffer_;
Mutex mtx_; // controls access to buffer_
};
Inside a thread function, you simply use a LockingPtr to get
controlled access to the buffer_ member variable:
void SyncBuf::Thread1() {
LockingPtr lpBuf(buffer_, mtx_);
BufT::iterator i = lpBuf->begin();
for (; i != lpBuf->end(); ++i) {
... use *i ...
}
}
The code is very easy to write and understand — whenever you need to use
buffer_, you must create a LockingPtr pointing to it. Once you do that
, you have access to vector's entire interface.
The nice part is that if you make a mistake, the compiler will point it out:
void SyncBuf::Thread2() {
// Error! Cannot access 'begin' for a volatile object
BufT::iterator i = buffer_.begin();
// Error! Cannot access 'end' for a volatile object
for (; i != lpBuf->end(); ++i) {
... use *i ...
}
}
You cannot access any function of buffer_ until you either apply a const_
cast or use LockingPtr. The difference is that LockingPtr offers an ordered
way of applying const_cast to volatile variables.
LockingPtr is remarkably expressive. If you only need to call one function,
you can create an unnamed temporary LockingPtr object and use it directly:
unsigned int SyncBuf::Size() {
return LockingPtr(buffer_, mtx_)->size();
} | k*******t 发帖数: 202 | | k*******t 发帖数: 202 | | B********8 发帖数: 9 | 4 Are they from this page: http://drdobbs.com/cpp/184403766?
There are basically two points here:
1) LockPtr is a smartptr. It's ctor will call Mutex.aquire(); and its dtor
will call Mutex.release().
2) (This is harder) The smart usage of "volatile" and "const_cast<>" to make
user defined class variable/function thread-safe.
a) "volatile" object can NOT access normal members, such as "begin()", "
end()". It must be cast to "const" using const_cast<>
b) LockPtr will do this cast for you, when it overload "->" and "*"
operators.
Hope this help. |
|