Here is my commandline test program which demonstrates how to
use the ThreadSafeObject in an application.
It can be compiled verbatim, or split it into its .h and .cpp parts.
Requires the file ThreadSafeObject.h from
http://code.axter.com/ThreadSafeObject.h
/*
TestCase1 v1.00
Testing 'ThreadSafeObject' of David Maisonave (
http://code.axter.com/ThreadSafeObject.h)
Author ; U.Mutlu (uenal.mutlu at t-online.de)
Date : 050424Su
Compiler: VC++6 and later versions
AppType : Console application
Requirements:
- Get the file ThreadSafeObject.h from
http://code.axter.com/ThreadSafeObject.h
- You can move the header part of the source below to TestCase1.h, but it's
not necessary for the purpose of this testing.
Compile and Link:
CL /GX /W3 /MD /Od TestCase1.cpp
What it does:
It populates a vector with 5 million items (each about 8 bytes) by 25
simultanously running threads (all do the same job, ie. adding items
at the end of the shared vector). The items are ascendingly numbered
so in the end we can check for consistency of the data (see code).
All threads do wait for a signal from the main thread before they
start their jobs.
After the collective job is done the main thread checks for consistency
and prints a report.
Locking of the vector happens "interlocked", ie. only for the duration
of adding a single item. By this method each thread has equal chance
to access the shared vector.
In the end the report shows how many items each thread has created
and whether the data is consistent or not.
In main() you can change nThreads and nItems.
*/
//--------------------------------------------------------------------
// this should go to TestCase1.h
#ifndef TestCase1_h
#define TestCase1_h
#include <windows.h>
#include "ThreadSafeObject.h" // see
http://code.axter.com/ThreadSafeObject.h
#include <cassert>
#include <vector>
#include <iostream>
#include <conio.h> // for kbhit(), getch()
struct TSMyItem
{
int iVal;
int iThr; // creator of the element; 0 to nMaxThreads-1
};
class TCTestCase1
{
public:
TCTestCase1(int AnMaxItems, int AnMaxThreads);
~TCTestCase1();
void Start();
void Stop();
bool IsRunning() { return vectThr.GetLockedObject()->size() > 0; }
int CheckConsistency(bool AfDump = true); // dbg func
private:
const int nMaxThreads;
const int nMaxItems;
volatile bool fBegin, fQuit;
ThreadSafeObject<std::vector<TSMyItem> > vect;
ThreadSafeObject<std::vector<int> > vectThr; // 0 to nMaxThreads-1
static DWORD WINAPI ThreadProc(void* ApPar);
void ThreadProc_sub(int AiThr);
// no need to lock this b/c it is not accessed by multiple threads
std::vector<int> vectItemsPerThread;
};
#endif
//--------------------------------------------------------------------
// this should go to TestCase1.cpp:
// #include "TestCase1.h"
struct TSThrPar
{
int iThr;
void* pThis;
volatile bool fStarted;
TSThrPar(int AiThr, void* ApThis) : iThr(AiThr), pThis(ApThis), fStarted(false) {}
};
TCTestCase1::TCTestCase1(int AnMaxItems, int AnMaxThreads)
: nMaxItems(AnMaxItems),
nMaxThreads(AnMaxThreads),
fBegin(false),
fQuit(false),
vect(new std::vector<TSMyItem>),
vectThr(new std::vector<int>),
vectItemsPerThread(nMaxThreads)
{}
TCTestCase1::~TCTestCase1()
{
Stop();
}
void TCTestCase1::Start()
{
fQuit = false;
fBegin = false;
for (int i = 0;i < nMaxThreads; i++)
{
TSThrPar SPar(i, this);
DWORD dwThreadId;
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, &SPar, 0, &dwThreadId);
while (!SPar.fStarted)
Sleep(1); // wait till thread sets fStarted
}
fBegin = true; // threads begin their work after this signal
}
void TCTestCase1::Stop()
{
fQuit = true;
while (vectThr.GetLockedObject()->size())
Sleep(100);
}
void TCTestCase1::ThreadProc_sub(int AiThr)
{
while (!fQuit)
{
// on demand locking on element-wise access need
ThreadSafeObject<std::vector<TSMyItem> >::RefLock lockedVect = vect.GetLockedObject();
TSMyItem S;
S.iVal = lockedVect->size() + 1; // this method helps us to check consistency at the end
S.iThr = AiThr;
if (S.iVal > nMaxItems)
break;
lockedVect->push_back(S);
}
}
DWORD WINAPI TCTestCase1::ThreadProc(void* ApPar)
{
TSThrPar* ApSPar = (TSThrPar*) ApPar;
TCTestCase1& rThis = *(TCTestCase1*) ApSPar->pThis;
int iThr = ApSPar->iThr;
rThis.vectThr.GetLockedObject()->push_back(iThr);
ApSPar->fStarted = true; // tell caller to continue; after this ApSPar becomes invalid
while (!rThis.fBegin)
Sleep(10);
rThis.ThreadProc_sub(iThr);
rThis.vectThr.GetLockedObject()->pop_back(); // only the size() is important...
return 0;
}
int TCTestCase1::CheckConsistency(bool AfDump) // dbg func
{ /* rc: 0=ok, -1=data consistency failed, -2=data was produced by 1 thread only,
-3=test still running (threads not finished)
the vector must be filled by at least 2 threads;
otherwise increase nMaxItems and/or nMaxThreads and try again
*/
if (IsRunning())
return -3; // Stop() must have been invoked before calling this
// clear vector:
size_t i;
for (i = 0; i < vectItemsPerThread.size(); i++)
vectItemsPerThread
= 0;
ThreadSafeObject<std::vector<TSMyItem> >::RefLock lockedVect = vect.GetLockedObject();
bool fBad = false;
vectItemsPerThread[lockedVect->at(0).iThr] += 1;
for (i = 1; i < lockedVect->size(); i++)
{
TSMyItem& rS = lockedVect->at(i);
TSMyItem& rSprev = lockedVect->at(i - 1);
// count items created by each thread:
vectItemsPerThread[rS.iThr] += 1;
// checking consistency:
// 1) all items must be in ascending order,
// 2) must not have any gaps,
// 3) and also no dupes
if ((rS.iVal - rSprev.iVal) != 1)
{
fBad = true;
if (!AfDump)
break;
}
}
// dump num items created by each thread:
if (AfDump)
for (i = 0; i < vectItemsPerThread.size(); i++)
std::cout << "Thr" << i << ": " << vectItemsPerThread << std::endl;
if (fBad)
return -1;
// check whether the data was created by more than 1 thread
int cProducer = 0;
for (i = 0; i < vectItemsPerThread.size(); i++)
if (vectItemsPerThread)
cProducer++;
return cProducer < 2 ? -2 : 0;
}
//--------------------------------------------------------------------
int main(int argc, char* argv[])
{
const int nItems = 5000000; // about 38 MB
const int nThreads = 25;
std::cout << "Testing 'ThreadSafeObject' of David Maisonave "
"(http://code.axter.com/ThreadSafeObject.h)" << std::endl;
std::cout << "Creating " << nItems << " items using "
<< nThreads << " threads." << std::endl;
TCTestCase1 TC(nItems, nThreads);
TC.Start();
// it will Stop() automatically after nItems were added
// but we can also manually break by pressing the Esc key:
while (TC.IsRunning())
{
if (kbhit() && (getch() == 27))
break;
Sleep(1000);
}
TC.Stop();
int rc = TC.CheckConsistency(true);
if (rc == -1)
std::cout << "Data consistency failed!" << std::endl;
else if (rc == -2)
std::cout << "Data was produced by 1 thread only. "
"Increase nMaxItems and nMaxThreads and try again" << std::endl;
else if (rc == -3)
std::cout << "Some threads still running." << std::endl; // cannot happen
else if (rc == 0)
std::cout << "All tests passed successfully." << std::endl;
else
std::cout << "Unknown return code " << rc << std::endl;
return 0;
}
//--------------------------------------------------------------------