How to do THIS with boost::mutex/locks...

Started by
4 comments, last by Yann L 16 years, 11 months ago
I have a object that starts out in a 'unlocked' state. A writer thread can write something to the object, then 'lock' the object (by lock I mean no more WRITING, but it IS safe to READ). Now, I want the object to REMAIN locked until a reader thread processes the object, and unlocks it. This isnt possible with boosts scoped locks, so I came up with this ghetto solution...

	class MyClass {
		public:
			MyClass() : mIsLocked(0) {}
			void writeStuff( void ) {
				//if the object is locked, wait for it to be unlocked
				while ( mIsLocked ) {}
				//write stuff here
				//...
				//lock the object so it can be read
				mIsLocked = true;
			}
			void readStuff( void ) {
				if ( !mIsLocked ) return;
				//read stuff here
				//...
				//unlock the object so it can be written to again
				mIsLocked = false;
			}

		private:
			bool mIsLocked;
	};
The above works. Yes its ugly, yes its dangerous, but assuming there is only one writer thread, and one reader thread, it will 'work'. My question is, whats the 'proper' way to do this? Using boost of course! Performance is key too. Thanks in advance!
Advertisement
Quote:A writer thread can write something to the object, then 'lock' the object. Now, I want the object to REMAIN locked until a reader thread processes the object, and unlocks it...
My question is, whats the 'proper' way to do this?

Have you every heard of the producer/consumer threading pattern?

http://en.wikipedia.org/wiki/Producers-consumers_problem

I don't know boost but basically you want to look at something that comes under a variety of names:
monitor
condition variable
condition mutex
condition event

This is the safest way when there are multiple readers and writers(plus there are other patterns which do the job), without further information it is hard to say what is the best really.

Quote:The above works.
I would highly doubt it, proving threading patterns work is not as simple as you seem to think.

[edit]links added
http://www.boost.org/doc/html/thread.html
http://www.boost.org/boost/thread/condition.hpp

It seems boost just settled for the name condition :)
Hrmm ok so something like this then?

	class MyClass {		public:			MyClass() : mIsFull(0) {}			void writeStuff( void ) {				//secure a lock (to make sure readStuff isnt running)				boost::mutex::scoped_lock lock( mutex );				//if the object is full, wait for it to be emptied				if ( mIsFull ) condition.wait( lock )				//write stuff here				//...				//mark the object as full				mIsFull = true;			}			void readStuff( void ) {				//secure a lock (to make sure writeStuff isnt running)				boost::mutex::scoped_lock lock( mutex );				//read stuff here (if theres anything to read)				//...				//unlock the object so it can be written to again				mIsFull = false;				condition.notify_all();			}		private:			bool mIsFull;			boost::mutex mutex;			boost::condition condition;	};


The only problem is, notify_all seems to be REALLY slow (relative to locking a mutex at least). Is there anyway around calling notify_all EVERY time readStuff is executed?
Can't you use notify_one?

I'm not sure exactly what you're trying to do, but generally with queues, it's enough to notify one only.

The fastest way for queues is obviously lockless queues, but those are platform specific.

Quote:The only problem is, notify_all seems to be REALLY slow (relative to locking a mutex at least). Is there anyway around calling notify_all EVERY time readStuff is executed?


Yes, - batch reads/writes.

Rather than handling single event, you post as many as possible, even if at danger of overflowing the buffer. The queue then absorbes as many as it has room for. Perhaps using std:list from which the queue removes the elements it has absorbed.

Reader threads rather than taking one single object off queue take as many as possible once again. A vector or some other fixed structure which accepts "up to" certain number of elements.

But this might not be suitable for your needs.


Bottom line is: The less locking you do, the better the concurrency. Locking for each object is indeed expensive.
There doesnt seem to be much of a performance difference between notify_one/notify_all. Its just that calling notify_one 50 times takes about 0.2ms on my machine, which is INSANELY slow (compared to 50 locks, which only takes about 0.003ms).

Even if I cram more messages into each object/queue, im still going to have to call notify_one at least 20-30 times per loop.. I just wish there was a better way..

I think I just need a way to have a 'central' lock, that only the writer can lock, and only the reader can unlock. Isnt that what a boost::read/write_mutex does? I heard about them, but they arent included in the sdk for some reason...
Quote:Original post by ZealousEngine
There doesnt seem to be much of a performance difference between notify_one/notify_all. Its just that calling notify_one 50 times takes about 0.2ms on my machine, which is INSANELY slow (compared to 50 locks, which only takes about 0.003ms).

Condition variables aren't natively available under XP, so they have to be emulated, which is terribly inefficient. Vista finally supplies native CV support, which should solve this problem. However, CVs will always be slower than a simple mutex or semaphore lock.

Note that there is another way to simulate CVs (using the SignalObjectAndWait API), that should be quite a bit faster than the one used in Boost right now.

Quote:Original post by ZealousEngine
Isnt that what a boost::read/write_mutex does? I heard about them, but they arent included in the sdk for some reason...

The reason is that they're broken. RW mutexes are insanely hard to get working right.

Boost::threads was scheduled for a major rewrite for some time now, but I don't know what the current status is. I think the original author left the project a while ago, and it has been floating since. The fact that the codebase is a *huge* mess doesn't really help either...

This topic is closed to new replies.

Advertisement