Monthly Archives: July 2010

Poco::Mutex

Poco::MutexPoco::FastMutex を紹介します。

両者ともマルチスレッド環境での共有リソースのアクセスの同期に使います。
Poco::Mutex は、同じスレッドから何度もロックしても問題ありませんが、Poco::FastMutex は、既にロックしている時に同じスレッドから再びロックするとデッドロックします。

MutexTest.cpp

・スレッドを5つ立て、それぞれで同一のカウンタを複数回 read/write し、カウンタの最終値に矛盾が
 無いことを確認し、処理時間も計測。
Poco::MutexPoco::FastMutex の両者で同じテストを実行。
Poco::Mutex::lock()Poco::Mutex::unlock()Poco::FastMutex::lock()Poco::FastMutex::unlock()
 の組み合わせでももちろん動作するが、RAII イディオムを使った Poco::ScopedLock を利用。

#include <Poco/Format.h>
#include <Poco/Runnable.h>
#include <Poco/Thread.h>
#include <Poco/Mutex.h>
#include <Poco/ScopedLock.h>
#include <Poco/Stopwatch.h>
 
#include <string>
 
#include "ScopedLogMessage.h"
#include "PrepareConsoleLogger.h"
 
const int kNumLoops = 1000;
const int kNumRunnable = 5;
 
template <class T>
class MutexRunnable: public Poco::Runnable
{
public:
	MutexRunnable(T& lock, volatile int& counter) :
		m_Lock(lock)
	,	m_Counter(counter)
	,	m_Ok(true)
	{
	}
 
	void run()
	{
		int lastCount = 0;
		for(int i=0; i<kNumLoops; ++i)
		{
			{	// scope for read lock
				Poco::ScopedLock<T> lock(m_Lock);
 
				lastCount = m_Counter;
				for(int k=0; k<100; ++k)
				{
					if(m_Counter != lastCount)
					{
						m_Ok = false;
					}
					Poco::Thread::yield();
				}
			}
 
			{	// scope for write lock
				Poco::ScopedLock<T> lock(m_Lock);
 
				for(int k=0; k<100; ++k)
				{
					--m_Counter;
					Poco::Thread::yield();
				}
				for(int k=0; k<100; ++k)
				{
					++m_Counter;
					Poco::Thread::yield();
				}
				++m_Counter;
				if(m_Counter <= lastCount)
				{
					m_Ok = false;
				}
			}
		}
	}
 
	bool ok() const
	{
		return m_Ok;
	}
 
private:
	T&		m_Lock;
	volatile int&	m_Counter;
	bool		m_Ok;
};
 
template <class T>
void TestMutex(ScopedLogMessage& msg, const std::string& name)
{
	msg.Message(Poco::format(" --- %s ---", name));
 
	Poco::Stopwatch stopwatch;
	stopwatch.start();
 
	T lock;
	int counter = 0;
 
	MutexRunnable<T>* pRunnable[kNumRunnable];
	Poco::Thread thread[kNumRunnable];
 
	for(int i=0; i<kNumRunnable; ++i)
	{
		pRunnable[i] = new MutexRunnable<T>(lock, counter);
		thread[i].start(*pRunnable[i]);
	}
 
	for(int i=0; i<kNumRunnable; ++i)
	{
		thread[i].join();
		if(!pRunnable[i]->ok())
		{
			msg.Message(Poco::format(" MutexRunnable[%i] failed!", i));
		}
		delete pRunnable[i];
	}
 
	stopwatch.stop();
 
	msg.Message(Poco::format("  counter = %i (expected: %i)", counter, kNumLoops*kNumRunnable));
	msg.Message(Poco::format("  Elapsed time = %.3fmSec", (1000.0 * stopwatch.elapsed()) / stopwatch.resolution()));
}
 
int main(int /*argc*/, char** /*argv*/)
{
	PrepareConsoleLogger logger(Poco::Logger::ROOT, Poco::Message::PRIO_INFORMATION);
 
	ScopedLogMessage msg("MutexTest ", "start", "end");
 
	TestMutex<Poco::Mutex>(msg, "Poco::Mutex test");
	TestMutex<Poco::FastMutex>(msg, "Poco::FastMutex test");
 
	return 0;
}

Results of execution

- On Linux

[0] MutexTest start
[0]  --- Poco::Mutex test ---
[0]   counter = 5000 (expected: 5000)
[0]   Elapsed time = 2387.954mSec
[0]  --- Poco::FastMutex test ---
[0]   counter = 5000 (expected: 5000)
[0]   Elapsed time = 2142.277mSec
[0] MutexTest end

- On Macintosh OSX 10.6.4

[0] MutexTest start
[0]  --- Poco::Mutex test ---
[0]   counter = 5000 (expected: 5000)
[0]   Elapsed time = 990.828mSec
[0]  --- Poco::FastMutex test ---
[0]   counter = 5000 (expected: 5000)
[0]   Elapsed time = 989.493mSec
[0] MutexTest end

- On Windows XP sp3

[0] MutexTest start
[0]  --- Poco::Mutex test ---
[0]   counter = 5000 (expected: 5000)
[0]   Elapsed time = 2765.625mSec
[0]  --- Poco::FastMutex test ---
[0]   counter = 5000 (expected: 5000)
[0]   Elapsed time = 1359.375mSec
[0] MutexTest end

Downloads

ここをクリックすると、makefile や VC++ プロジェクトなど一式がダウンロードできます。
(2013.05.31 updated)
・2010年7月31日からのダウンロード数:840

Subversion

・フリーの Subversion ホスティングサービス Assemblaで、ソースコードを管理しています。

Reference

http://pocoproject.org にある Threads のプレセンテーション。(PDF)

Powered by POCO Copyright © 2010 Round Square Inc. All rights reserved.